// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//! Proof of work consensus for Substrate.
//!
//! To use this engine, you can need to have a struct that implements
//! [`PowAlgorithm`]. After that, pass an instance of the struct, along
//! with other necessary client references to [`import_queue`] to setup
//! the queue.
//!
//! This library also comes with an async mining worker, which can be
//! started via the [`start_mining_worker`] function. It returns a worker
//! handle together with a future. The future must be pulled. Through
//! the worker handle, you can pull the metadata needed to start the
//! mining process via [`MiningHandle::metadata`], and then do the actual
//! mining on a standalone thread. Finally, when a seal is found, call
//! [`MiningHandle::submit`] to build the block.
//!
//! The auxiliary storage for PoW engine only stores the total difficulty.
//! For other storage requirements for particular PoW algorithm (such as
//! the actual difficulty for each particular blocks), you can take a client
//! reference in your [`PowAlgorithm`] implementation, and use a separate prefix
//! for the auxiliary storage. It is also possible to just use the runtime
//! as the storage, but it is not recommended as it won't work well with light
//! clients.
mod worker;
pub use crate::worker::{MiningBuild, MiningHandle, MiningMetadata};
use crate::worker::UntilImportedOrTimeout;
use codec::{Decode, Encode};
use futures::{Future, StreamExt};
use log::*;
use prometheus_endpoint::Registry;
use sc_client_api::{self, backend::AuxStore, BlockOf, BlockchainEvents};
use sc_consensus::{
BasicQueue, BlockCheckParams, BlockImport, BlockImportParams, BoxBlockImport,
BoxJustificationImport, ForkChoiceStrategy, ImportResult, Verifier,
};
use sp_api::ProvideRuntimeApi;
use sp_block_builder::BlockBuilder as BlockBuilderApi;
use sp_blockchain::HeaderBackend;
use sp_consensus::{Environment, Error as ConsensusError, Proposer, SelectChain, SyncOracle};
use sp_consensus_pow::{Seal, TotalDifficulty, POW_ENGINE_ID};
use sp_inherents::{CreateInherentDataProviders, InherentDataProvider};
use sp_runtime::{
generic::{BlockId, Digest, DigestItem},
traits::{Block as BlockT, Header as HeaderT},
RuntimeString,
};
use std::{cmp::Ordering, marker::PhantomData, sync::Arc, time::Duration};
const LOG_TARGET: &str = "pow";
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Header uses the wrong engine {0:?}")]
WrongEngine([u8; 4]),
#[error("Header {0:?} is unsealed")]
HeaderUnsealed(B::Hash),
#[error("PoW validation error: invalid seal")]
InvalidSeal,
#[error("PoW validation error: preliminary verification failed")]
FailedPreliminaryVerify,
#[error("Rejecting block too far in future")]
TooFarInFuture,
#[error("Fetching best header failed using select chain: {0}")]
BestHeaderSelectChain(ConsensusError),
#[error("Fetching best header failed: {0}")]
BestHeader(sp_blockchain::Error),
#[error("Best header does not exist")]
NoBestHeader,
#[error("Block proposing error: {0}")]
BlockProposingError(String),
#[error("Fetch best hash failed via select chain: {0}")]
BestHashSelectChain(ConsensusError),
#[error("Error with block built on {0:?}: {1}")]
BlockBuiltError(B::Hash, ConsensusError),
#[error("Creating inherents failed: {0}")]
CreateInherents(sp_inherents::Error),
#[error("Checking inherents failed: {0}")]
CheckInherents(sp_inherents::Error),
#[error(
"Checking inherents unknown error for identifier: {}",
String::from_utf8_lossy(.0)
)]
CheckInherentsUnknownError(sp_inherents::InherentIdentifier),
#[error("Multiple pre-runtime digests")]
MultiplePreRuntimeDigests,
#[error(transparent)]
Client(sp_blockchain::Error),
#[error(transparent)]
Codec(codec::Error),
#[error("{0}")]
Environment(String),
#[error("{0}")]
Runtime(RuntimeString),
#[error("{0}")]
Other(String),
}
impl From> for String {
fn from(error: Error) -> String {
error.to_string()
}
}
impl From> for ConsensusError {
fn from(error: Error) -> ConsensusError {
ConsensusError::ClientImport(error.to_string())
}
}
/// Auxiliary storage prefix for PoW engine.
pub const POW_AUX_PREFIX: [u8; 4] = *b"PoW:";
/// Get the auxiliary storage key used by engine to store total difficulty.
fn aux_key>(hash: &T) -> Vec {
POW_AUX_PREFIX.iter().chain(hash.as_ref()).copied().collect()
}
/// Intermediate value passed to block importer.
#[derive(Encode, Decode, Clone, Debug, Default)]
pub struct PowIntermediate {
/// Difficulty of the block, if known.
pub difficulty: Option,
}
/// Intermediate key for PoW engine.
pub static INTERMEDIATE_KEY: &[u8] = b"pow1";
/// Auxiliary storage data for PoW.
#[derive(Encode, Decode, Clone, Debug, Default)]
pub struct PowAux {
/// Difficulty of the current block.
pub difficulty: Difficulty,
/// Total difficulty up to current block.
pub total_difficulty: Difficulty,
}
impl PowAux
where
Difficulty: Decode + Default,
{
/// Read the auxiliary from client.
pub fn read(client: &C, hash: &B::Hash) -> Result> {
let key = aux_key(&hash);
match client.get_aux(&key).map_err(Error::Client)? {
Some(bytes) => Self::decode(&mut &bytes[..]).map_err(Error::Codec),
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.
///
/// This function will be called twice during the import process, so the implementation
/// should be properly cached.
fn difficulty(&self, parent: B::Hash) -> Result>;
/// Verify that the seal is valid against given pre hash when parent block is not yet imported.
///
/// None means that preliminary verify is not available for this algorithm.
fn preliminary_verify(
&self,
_pre_hash: &B::Hash,
_seal: &Seal,
) -> Result