Skip to content
Snippets Groups Projects
Commit ba52f894 authored by André Silva's avatar André Silva
Browse files

client: add a block blacklist extension

parent 597df6eb
Branches
Tags
No related merge requests found
......@@ -22,7 +22,8 @@
"tokenDecimals": 15,
"tokenSymbol": "FIR"
},
"fork_blocks": null,
"forkBlocks": null,
"badBlocks": null,
"consensusEngine": null,
"genesis": {
"raw": {
......@@ -119,4 +120,4 @@
"children": {}
}
}
}
\ No newline at end of file
}
......@@ -47,9 +47,12 @@ const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
/// Additional parameters for some Substrate core modules,
/// customizable from the chain spec.
#[derive(Default, Clone, Serialize, Deserialize, ChainSpecExtension)]
#[serde(rename_all = "camelCase")]
pub struct Extensions {
/// Block numbers with known hashes.
pub fork_blocks: sc_client::ForkBlocks<Block>,
/// Known bad block hashes.
pub bad_blocks: sc_client::BadBlocks<Block>,
}
/// Specialized `ChainSpec`.
......
......@@ -16,6 +16,7 @@
//! A set of APIs supported by the client along with their primitives.
use std::collections::HashSet;
use futures::channel::mpsc;
use sp_core::storage::StorageKey;
use sp_runtime::{
......@@ -36,9 +37,16 @@ pub type FinalityNotifications<Block> = mpsc::UnboundedReceiver<FinalityNotifica
/// Expected hashes of blocks at given heights.
///
/// This may be used as chain spec extension to filter out known, unwanted forks.
/// This may be used as chain spec extension to set trusted checkpoints, i.e.
/// the client will refuse to import a block with a different hash at the given
/// height.
pub type ForkBlocks<Block> = Option<Vec<(NumberFor<Block>, <Block as BlockT>::Hash)>>;
/// Known bad block hashes.
///
/// This may be used as chain spec extension to filter out known, unwanted forks.
pub type BadBlocks<Block> = Option<HashSet<<Block as BlockT>::Hash>>;
/// Figure out the block type for a given type (for now, just a `Client`).
pub trait BlockOf {
/// The type of the block.
......
......@@ -39,7 +39,7 @@ use std::path::PathBuf;
use std::io;
use std::collections::{HashMap, HashSet};
use sc_client_api::{execution_extensions::ExecutionExtensions, ForkBlocks};
use sc_client_api::{execution_extensions::ExecutionExtensions, BadBlocks, ForkBlocks};
use sc_client_api::backend::NewBlockState;
use sc_client_api::backend::{StorageCollection, ChildStorageCollection};
use sp_blockchain::{
......@@ -276,6 +276,7 @@ pub fn new_client<E, S, Block, RA>(
executor: E,
genesis_storage: S,
fork_blocks: ForkBlocks<Block>,
bad_blocks: BadBlocks<Block>,
execution_extensions: ExecutionExtensions<Block>,
) -> Result<(
sc_client::Client<
......@@ -296,7 +297,14 @@ pub fn new_client<E, S, Block, RA>(
let backend = Arc::new(Backend::new(settings, CANONICALIZATION_DELAY)?);
let executor = sc_client::LocalCallExecutor::new(backend.clone(), executor);
Ok((
sc_client::Client::new(backend.clone(), executor, genesis_storage, fork_blocks, execution_extensions)?,
sc_client::Client::new(
backend.clone(),
executor,
genesis_storage,
fork_blocks,
bad_blocks,
execution_extensions,
)?,
backend,
))
}
......
......@@ -181,6 +181,12 @@ fn new_full_parts<TBl, TRtApi, TExecDisp, TCfg, TGen, TCSExt>(
.cloned()
.unwrap_or_default();
let bad_blocks = config.chain_spec
.extensions()
.get::<sc_client::BadBlocks<TBl>>()
.cloned()
.unwrap_or_default();
let (client, backend) = {
let db_config = sc_client_db::DatabaseSettings {
state_cache_size: config.state_cache_size,
......@@ -208,6 +214,7 @@ fn new_full_parts<TBl, TRtApi, TExecDisp, TCfg, TGen, TCSExt>(
executor,
&config.chain_spec,
fork_blocks,
bad_blocks,
extensions,
)?
};
......
......@@ -69,7 +69,7 @@ pub use sc_client_api::{
},
client::{
ImportNotifications, FinalityNotification, FinalityNotifications, BlockImportNotification,
ClientInfo, BlockchainEvents, BlockBody, ProvideUncles, ForkBlocks,
ClientInfo, BlockchainEvents, BlockBody, ProvideUncles, BadBlocks, ForkBlocks,
BlockOf,
},
execution_extensions::{ExecutionExtensions, ExecutionStrategies},
......@@ -101,6 +101,7 @@ pub struct Client<B, E, Block, RA> where Block: BlockT {
// holds the block hash currently being imported. TODO: replace this with block queue
importing_block: RwLock<Option<Block::Hash>>,
fork_blocks: ForkBlocks<Block>,
bad_blocks: BadBlocks<Block>,
execution_extensions: ExecutionExtensions<Block>,
_phantom: PhantomData<RA>,
}
......@@ -174,7 +175,14 @@ pub fn new_with_backend<B, E, Block, S, RA>(
{
let call_executor = LocalCallExecutor::new(backend.clone(), executor);
let extensions = ExecutionExtensions::new(Default::default(), keystore);
Client::new(backend, call_executor, build_genesis_storage, Default::default(), extensions)
Client::new(
backend,
call_executor,
build_genesis_storage,
Default::default(),
Default::default(),
extensions,
)
}
impl<B, E, Block, RA> BlockOf for Client<B, E, Block, RA> where
......@@ -196,6 +204,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
executor: E,
build_genesis_storage: S,
fork_blocks: ForkBlocks<Block>,
bad_blocks: BadBlocks<Block>,
execution_extensions: ExecutionExtensions<Block>,
) -> sp_blockchain::Result<Self> {
if backend.blockchain().header(BlockId::Number(Zero::zero()))?.is_none() {
......@@ -225,6 +234,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
finality_notification_sinks: Default::default(),
importing_block: Default::default(),
fork_blocks,
bad_blocks,
execution_extensions,
_phantom: Default::default(),
})
......@@ -1486,6 +1496,8 @@ impl<'a, B, E, Block, RA> sp_consensus::BlockImport<Block> for &'a Client<B, E,
) -> Result<ImportResult, Self::Error> {
let BlockCheckParams { hash, number, parent_hash, allow_missing_state, import_existing } = block;
// Check the block against white and black lists if any are defined
// (i.e. fork blocks and bad blocks respectively)
let fork_block = self.fork_blocks.as_ref()
.and_then(|fs| fs.iter().find(|(n, _)| *n == number));
......@@ -1501,6 +1513,19 @@ impl<'a, B, E, Block, RA> sp_consensus::BlockImport<Block> for &'a Client<B, E,
}
}
let bad_block = self.bad_blocks.as_ref()
.filter(|bs| bs.contains(&hash))
.is_some();
if bad_block {
trace!(
"Rejecting known bad block: #{} {:?}",
number,
hash,
);
return Ok(ImportResult::KnownBad);
}
// Own status must be checked first. If the block and ancestry is pruned
// this function must return `AlreadyInChain` rather than `MissingState`
match self.block_status(&BlockId::Hash(hash))
......
......@@ -98,7 +98,7 @@ pub use crate::{
new_in_mem,
BlockBody, ImportNotifications, FinalityNotifications, BlockchainEvents,
BlockImportNotification, Client, ClientInfo, ExecutionStrategies, FinalityNotification,
LongestChain, BlockOf, ProvideUncles, ForkBlocks, apply_aux,
LongestChain, BlockOf, ProvideUncles, BadBlocks, ForkBlocks, apply_aux,
},
leaves::LeafSet,
};
......
......@@ -70,7 +70,14 @@ pub fn new_light<B, S, GS, RA, E>(
{
let local_executor = LocalCallExecutor::new(backend.clone(), code_executor);
let executor = GenesisCallExecutor::new(backend.clone(), local_executor);
Client::new(backend, executor, genesis_storage, Default::default(), Default::default())
Client::new(
backend,
executor,
genesis_storage,
Default::default(),
Default::default(),
Default::default(),
)
}
/// Create an instance of fetch data checker.
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment