mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 04:11:07 +00:00
Allow for customisation of chain selection systems (#2240)
* move SelectChain trait out of client * Extend SelectChain, move longest chain implementation into it * Bring SelectChain into service * implement LongestChain SelectChain * implement longest chain for node * update Cargo.lock's * in between erroring tests * deprecate ::backend and ::import_lock * Remove unneded space Co-Authored-By: gnunicorn <ben.kampmann@googlemail.com> * Remove unneded space Co-Authored-By: gnunicorn <ben.kampmann@googlemail.com> * Fixes test compilation * remove todo * re-enable client test * add doc * fixing tests * Clarify SelectChain Interface, intended implementation and usage * minor components cleanups * minor cleanups * Update lock files * Implement cleaner interface for SelectChain * addressing comments * Updating tests * bump node runtime impl version * address grumbles
This commit is contained in:
committed by
Gavin Wood
parent
32fdeed21c
commit
18ca0170c3
+394
-235
@@ -32,6 +32,7 @@ use consensus::{
|
||||
Error as ConsensusError, ErrorKind as ConsensusErrorKind, ImportBlock,
|
||||
ImportResult, BlockOrigin, ForkChoiceStrategy,
|
||||
well_known_cache_keys::Id as CacheKeyId,
|
||||
SelectChain, self,
|
||||
};
|
||||
use runtime_primitives::traits::{
|
||||
Block as BlockT, Header as HeaderT, Zero, As, NumberFor, CurrentHeight,
|
||||
@@ -71,7 +72,6 @@ use crate::error;
|
||||
use crate::in_mem;
|
||||
use crate::block_builder::{self, api::BlockBuilder as BlockBuilderAPI};
|
||||
use crate::genesis;
|
||||
use consensus;
|
||||
use substrate_telemetry::{telemetry, SUBSTRATE_INFO};
|
||||
|
||||
use log::{info, trace, warn};
|
||||
@@ -119,7 +119,7 @@ pub struct Client<B, E, Block, RA> where Block: BlockT {
|
||||
storage_notifications: Mutex<StorageNotifications<Block>>,
|
||||
import_notification_sinks: Mutex<Vec<mpsc::UnboundedSender<BlockImportNotification<Block>>>>,
|
||||
finality_notification_sinks: Mutex<Vec<mpsc::UnboundedSender<FinalityNotification<Block>>>>,
|
||||
import_lock: Mutex<()>,
|
||||
import_lock: Arc<Mutex<()>>,
|
||||
// holds the block hash currently being imported. TODO: replace this with block queue
|
||||
importing_block: RwLock<Option<Block::Hash>>,
|
||||
execution_strategies: ExecutionStrategies,
|
||||
@@ -149,15 +149,6 @@ pub trait BlockchainEvents<Block: BlockT> {
|
||||
fn storage_changes_notification_stream(&self, filter_keys: Option<&[StorageKey]>) -> error::Result<StorageEventStream<Block::Hash>>;
|
||||
}
|
||||
|
||||
/// Chain head information.
|
||||
pub trait ChainHead<Block: BlockT> {
|
||||
/// Get best block header.
|
||||
fn best_block_header(&self) -> Result<<Block as BlockT>::Header, error::Error>;
|
||||
/// Get all leaves of the chain: block hashes that have no children currently.
|
||||
/// Leaves that can never be finalized will not be returned.
|
||||
fn leaves(&self) -> Result<Vec<<Block as BlockT>::Hash>, error::Error>;
|
||||
}
|
||||
|
||||
/// Fetch block body by ID.
|
||||
pub trait BlockBody<Block: BlockT> {
|
||||
/// Get block body by ID. Returns `None` if the body is not stored.
|
||||
@@ -260,7 +251,7 @@ pub fn new_in_mem<E, Block, S, RA>(
|
||||
new_with_backend(Arc::new(in_mem::Backend::new()), executor, genesis_storage)
|
||||
}
|
||||
|
||||
/// Create a client with the explicitely provided backend.
|
||||
/// Create a client with the explicitly provided backend.
|
||||
/// This is useful for testing backend implementations.
|
||||
pub fn new_with_backend<B, E, Block, S, RA>(
|
||||
backend: Arc<B>,
|
||||
@@ -329,10 +320,23 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
}
|
||||
|
||||
/// Expose backend reference. To be used in tests only
|
||||
#[doc(hidden)]
|
||||
#[deprecated(note="Rather than relying on `client` to provide this, access \
|
||||
to the backend should be handled at setup only - see #1134. This function \
|
||||
will be removed once that is in place.")]
|
||||
pub fn backend(&self) -> &Arc<B> {
|
||||
&self.backend
|
||||
}
|
||||
|
||||
/// Expose reference to import lock
|
||||
#[doc(hidden)]
|
||||
#[deprecated(note="Rather than relying on `client` to provide this, access \
|
||||
to the backend should be handled at setup only - see #1134. This function \
|
||||
will be removed once that is in place.")]
|
||||
pub fn import_lock(&self) -> Arc<Mutex<()>> {
|
||||
self.import_lock.clone()
|
||||
}
|
||||
|
||||
/// Return storage entry keys in state in a block of given hash with given prefix.
|
||||
pub fn storage_keys(&self, id: &BlockId<Block>, key_prefix: &StorageKey) -> error::Result<Vec<StorageKey>> {
|
||||
let keys = self.state_at(id)?.keys(&key_prefix.0).into_iter().map(StorageKey).collect();
|
||||
@@ -1102,7 +1106,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
};
|
||||
match hash_and_number {
|
||||
Some((hash, number)) => {
|
||||
if self.backend().have_state_at(&hash, number) {
|
||||
if self.backend.have_state_at(&hash, number) {
|
||||
Ok(BlockStatus::InChainWithState)
|
||||
} else {
|
||||
Ok(BlockStatus::InChainPruned)
|
||||
@@ -1138,132 +1142,6 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
|
||||
})
|
||||
}
|
||||
|
||||
/// Get best block header.
|
||||
pub fn best_block_header(&self) -> error::Result<<Block as BlockT>::Header> {
|
||||
let info = self.backend.blockchain().info().map_err(|e| error::Error::from_blockchain(Box::new(e)))?;
|
||||
Ok(self.header(&BlockId::Hash(info.best_hash))?.expect("Best block header must always exist"))
|
||||
}
|
||||
|
||||
/// Get the most recent block hash of the best (longest) chains
|
||||
/// that contain block with the given `target_hash`.
|
||||
///
|
||||
/// The search space is always limited to blocks which are in the finalized
|
||||
/// chain or descendents of it.
|
||||
///
|
||||
/// If `maybe_max_block_number` is `Some(max_block_number)`
|
||||
/// the search is limited to block `numbers <= max_block_number`.
|
||||
/// in other words as if there were no blocks greater `max_block_number`.
|
||||
/// TODO : we want to move this implement to `blockchain::Backend`, see [#1443](https://github.com/paritytech/substrate/issues/1443)
|
||||
/// Returns `Ok(None)` if `target_hash` is not found in search space.
|
||||
/// TODO: document time complexity of this, see [#1444](https://github.com/paritytech/substrate/issues/1444)
|
||||
pub fn best_containing(&self, target_hash: Block::Hash, maybe_max_number: Option<NumberFor<Block>>)
|
||||
-> error::Result<Option<Block::Hash>>
|
||||
{
|
||||
let target_header = {
|
||||
match self.backend.blockchain().header(BlockId::Hash(target_hash))? {
|
||||
Some(x) => x,
|
||||
// target not in blockchain
|
||||
None => { return Ok(None); },
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(max_number) = maybe_max_number {
|
||||
// target outside search range
|
||||
if target_header.number() > &max_number {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
let (leaves, best_already_checked) = {
|
||||
// ensure no blocks are imported during this code block.
|
||||
// an import could trigger a reorg which could change the canonical chain.
|
||||
// we depend on the canonical chain staying the same during this code block.
|
||||
let _import_lock = self.import_lock.lock();
|
||||
|
||||
let info = self.backend.blockchain().info()?;
|
||||
|
||||
let canon_hash = self.backend.blockchain().hash(*target_header.number())?
|
||||
.ok_or_else(|| error::Error::from(format!("failed to get hash for block number {}", target_header.number())))?;
|
||||
|
||||
if canon_hash == target_hash {
|
||||
// if no block at the given max depth exists fallback to the best block
|
||||
if let Some(max_number) = maybe_max_number {
|
||||
if let Some(header) = self.backend.blockchain().hash(max_number)? {
|
||||
return Ok(Some(header));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(Some(info.best_hash));
|
||||
} else if info.finalized_number >= *target_header.number() {
|
||||
// header is on a dead fork.
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
(self.backend.blockchain().leaves()?, info.best_hash)
|
||||
};
|
||||
|
||||
// for each chain. longest chain first. shortest last
|
||||
for leaf_hash in leaves {
|
||||
// ignore canonical chain which we already checked above
|
||||
if leaf_hash == best_already_checked {
|
||||
continue;
|
||||
}
|
||||
|
||||
// start at the leaf
|
||||
let mut current_hash = leaf_hash;
|
||||
|
||||
// if search is not restricted then the leaf is the best
|
||||
let mut best_hash = leaf_hash;
|
||||
|
||||
// go backwards entering the search space
|
||||
// waiting until we are <= max_number
|
||||
if let Some(max_number) = maybe_max_number {
|
||||
loop {
|
||||
let current_header = self.backend.blockchain().header(BlockId::Hash(current_hash.clone()))?
|
||||
.ok_or_else(|| error::Error::from(format!("failed to get header for hash {}", current_hash)))?;
|
||||
|
||||
if current_header.number() <= &max_number {
|
||||
best_hash = current_header.hash();
|
||||
break;
|
||||
}
|
||||
|
||||
current_hash = *current_header.parent_hash();
|
||||
}
|
||||
}
|
||||
|
||||
// go backwards through the chain (via parent links)
|
||||
loop {
|
||||
// until we find target
|
||||
if current_hash == target_hash {
|
||||
return Ok(Some(best_hash));
|
||||
}
|
||||
|
||||
let current_header = self.backend.blockchain().header(BlockId::Hash(current_hash.clone()))?
|
||||
.ok_or_else(|| error::Error::from(format!("failed to get header for hash {}", current_hash)))?;
|
||||
|
||||
// stop search in this chain once we go below the target's block number
|
||||
if current_header.number() < target_header.number() {
|
||||
break;
|
||||
}
|
||||
|
||||
current_hash = *current_header.parent_hash();
|
||||
}
|
||||
}
|
||||
|
||||
// header may be on a dead fork -- the only leaves that are considered are
|
||||
// those which can still be finalized.
|
||||
//
|
||||
// FIXME #1558 only issue this warning when not on a dead fork
|
||||
warn!(
|
||||
"Block {:?} exists in chain but not found when following all \
|
||||
leaves backwards. Number limit = {:?}",
|
||||
target_hash,
|
||||
maybe_max_number,
|
||||
);
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Gets the uncles of the block with `target_hash` going back `max_generation` ancestors.
|
||||
pub fn uncles(&self, target_hash: Block::Hash, max_generation: NumberFor<Block>) -> error::Result<Vec<Block::Hash>> {
|
||||
let load_header = |id: Block::Hash| -> error::Result<Block::Header> {
|
||||
@@ -1515,14 +1393,168 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E, Block, RA> ChainHead<Block> for Client<B, E, Block, RA>
|
||||
/// Implement Longest Chain Select implementation
|
||||
/// where 'longest' is defined as the highest number of blocks
|
||||
pub struct LongestChain<B, Block> {
|
||||
backend: Arc<B>,
|
||||
import_lock: Arc<Mutex<()>>,
|
||||
_phantom: PhantomData<Block>
|
||||
}
|
||||
|
||||
impl<B, Block> Clone for LongestChain<B, Block> {
|
||||
fn clone(&self) -> Self {
|
||||
let backend = self.backend.clone();
|
||||
let import_lock = self.import_lock.clone();
|
||||
LongestChain {
|
||||
backend,
|
||||
import_lock,
|
||||
_phantom: Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, Block> LongestChain<B, Block>
|
||||
where
|
||||
B: backend::Backend<Block, Blake2Hasher>,
|
||||
E: CallExecutor<Block, Blake2Hasher>,
|
||||
Block: BlockT<Hash=H256>,
|
||||
{
|
||||
/// Instantiate a new LongestChain for Backend B
|
||||
pub fn new(backend: Arc<B>, import_lock: Arc<Mutex<()>>) -> Self {
|
||||
LongestChain {
|
||||
backend,
|
||||
import_lock,
|
||||
_phantom: Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn best_block_header(&self) -> error::Result<<Block as BlockT>::Header> {
|
||||
Client::best_block_header(self)
|
||||
let info : ChainInfo<Block> = match self.backend.blockchain().info() {
|
||||
Ok(i) => i,
|
||||
Err(e) => return Err(error::Error::from_blockchain(Box::new(e)))
|
||||
};
|
||||
Ok(self.backend.blockchain().header(BlockId::Hash(info.best_hash))?
|
||||
.expect("Best block header must always exist"))
|
||||
}
|
||||
|
||||
/// Get the most recent block hash of the best (longest) chains
|
||||
/// that contain block with the given `target_hash`.
|
||||
///
|
||||
/// The search space is always limited to blocks which are in the finalized
|
||||
/// chain or descendents of it.
|
||||
///
|
||||
/// If `maybe_max_block_number` is `Some(max_block_number)`
|
||||
/// the search is limited to block `numbers <= max_block_number`.
|
||||
/// in other words as if there were no blocks greater `max_block_number`.
|
||||
/// Returns `Ok(None)` if `target_hash` is not found in search space.
|
||||
/// TODO: document time complexity of this, see [#1444](https://github.com/paritytech/substrate/issues/1444)
|
||||
fn best_containing(
|
||||
&self,
|
||||
target_hash: Block::Hash,
|
||||
maybe_max_number: Option<NumberFor<Block>>
|
||||
) -> error::Result<Option<Block::Hash>> {
|
||||
let target_header = {
|
||||
match self.backend.blockchain().header(BlockId::Hash(target_hash))? {
|
||||
Some(x) => x,
|
||||
// target not in blockchain
|
||||
None => { return Ok(None); },
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(max_number) = maybe_max_number {
|
||||
// target outside search range
|
||||
if target_header.number() > &max_number {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
let (leaves, best_already_checked) = {
|
||||
// ensure no blocks are imported during this code block.
|
||||
// an import could trigger a reorg which could change the canonical chain.
|
||||
// we depend on the canonical chain staying the same during this code block.
|
||||
let _import_lock = self.import_lock.lock();
|
||||
|
||||
let info = self.backend.blockchain().info()?;
|
||||
|
||||
let canon_hash = self.backend.blockchain().hash(*target_header.number())?
|
||||
.ok_or_else(|| error::Error::from(format!("failed to get hash for block number {}", target_header.number())))?;
|
||||
|
||||
if canon_hash == target_hash {
|
||||
// if no block at the given max depth exists fallback to the best block
|
||||
if let Some(max_number) = maybe_max_number {
|
||||
if let Some(header) = self.backend.blockchain().hash(max_number)? {
|
||||
return Ok(Some(header));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(Some(info.best_hash));
|
||||
} else if info.finalized_number >= *target_header.number() {
|
||||
// header is on a dead fork.
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
(self.backend.blockchain().leaves()?, info.best_hash)
|
||||
};
|
||||
|
||||
// for each chain. longest chain first. shortest last
|
||||
for leaf_hash in leaves {
|
||||
// ignore canonical chain which we already checked above
|
||||
if leaf_hash == best_already_checked {
|
||||
continue;
|
||||
}
|
||||
|
||||
// start at the leaf
|
||||
let mut current_hash = leaf_hash;
|
||||
|
||||
// if search is not restricted then the leaf is the best
|
||||
let mut best_hash = leaf_hash;
|
||||
|
||||
// go backwards entering the search space
|
||||
// waiting until we are <= max_number
|
||||
if let Some(max_number) = maybe_max_number {
|
||||
loop {
|
||||
let current_header = self.backend.blockchain().header(BlockId::Hash(current_hash.clone()))?
|
||||
.ok_or_else(|| error::Error::from(format!("failed to get header for hash {}", current_hash)))?;
|
||||
|
||||
if current_header.number() <= &max_number {
|
||||
best_hash = current_header.hash();
|
||||
break;
|
||||
}
|
||||
|
||||
current_hash = *current_header.parent_hash();
|
||||
}
|
||||
}
|
||||
|
||||
// go backwards through the chain (via parent links)
|
||||
loop {
|
||||
// until we find target
|
||||
if current_hash == target_hash {
|
||||
return Ok(Some(best_hash));
|
||||
}
|
||||
|
||||
let current_header = self.backend.blockchain().header(BlockId::Hash(current_hash.clone()))?
|
||||
.ok_or_else(|| error::Error::from(format!("failed to get header for hash {}", current_hash)))?;
|
||||
|
||||
// stop search in this chain once we go below the target's block number
|
||||
if current_header.number() < target_header.number() {
|
||||
break;
|
||||
}
|
||||
|
||||
current_hash = *current_header.parent_hash();
|
||||
}
|
||||
}
|
||||
|
||||
// header may be on a dead fork -- the only leaves that are considered are
|
||||
// those which can still be finalized.
|
||||
//
|
||||
// FIXME #1558 only issue this warning when not on a dead fork
|
||||
warn!(
|
||||
"Block {:?} exists in chain but not found when following all \
|
||||
leaves backwards. Number limit = {:?}",
|
||||
target_hash,
|
||||
maybe_max_number,
|
||||
);
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn leaves(&self) -> Result<Vec<<Block as BlockT>::Hash>, error::Error> {
|
||||
@@ -1530,6 +1562,34 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, Block> SelectChain<Block> for LongestChain<B, Block>
|
||||
where
|
||||
B: backend::Backend<Block, Blake2Hasher>,
|
||||
Block: BlockT<Hash=H256>,
|
||||
{
|
||||
|
||||
fn leaves(&self) -> Result<Vec<<Block as BlockT>::Hash>, ConsensusError> {
|
||||
LongestChain::leaves(self)
|
||||
.map_err(|e| ConsensusErrorKind::ChainLookup(e.to_string()).into())
|
||||
}
|
||||
|
||||
fn best_chain(&self)
|
||||
-> Result<<Block as BlockT>::Header, ConsensusError>
|
||||
{
|
||||
LongestChain::best_block_header(&self)
|
||||
.map_err(|e| ConsensusErrorKind::ChainLookup(e.to_string()).into())
|
||||
}
|
||||
|
||||
fn finality_target(
|
||||
&self,
|
||||
target_hash: Block::Hash,
|
||||
maybe_max_number: Option<NumberFor<Block>>
|
||||
) -> Result<Option<Block::Hash>, ConsensusError> {
|
||||
LongestChain::best_containing(self, target_hash, maybe_max_number)
|
||||
.map_err(|e| ConsensusErrorKind::ChainLookup(e.to_string()).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E, Block, RA> BlockBody<Block> for Client<B, E, Block, RA>
|
||||
where
|
||||
B: backend::Backend<Block, Blake2Hasher>,
|
||||
@@ -1576,7 +1636,7 @@ pub(crate) mod tests {
|
||||
use runtime_primitives::traits::DigestItem as DigestItemT;
|
||||
use runtime_primitives::generic::DigestItem;
|
||||
use test_client::{self, TestClient, AccountKeyring};
|
||||
use consensus::BlockOrigin;
|
||||
use consensus::{BlockOrigin, SelectChain};
|
||||
use test_client::client::backend::Backend as TestBackend;
|
||||
use test_client::BlockBuilderExt;
|
||||
use test_client::runtime::{self, Block, Transfer, RuntimeApi, TestAPI};
|
||||
@@ -1753,8 +1813,14 @@ pub(crate) mod tests {
|
||||
let client = test_client::new();
|
||||
|
||||
let genesis_hash = client.info().unwrap().chain.genesis_hash;
|
||||
let longest_chain_select = test_client::client::LongestChain::new(
|
||||
client.backend().clone(),
|
||||
client.import_lock()
|
||||
);
|
||||
|
||||
assert_eq!(genesis_hash.clone(), client.best_containing(genesis_hash.clone(), None).unwrap().unwrap());
|
||||
|
||||
assert_eq!(genesis_hash.clone(), longest_chain_select.finality_target(
|
||||
genesis_hash.clone(), None).unwrap().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1765,8 +1831,13 @@ pub(crate) mod tests {
|
||||
let client = test_client::new();
|
||||
|
||||
let uninserted_block = client.new_block().unwrap().bake().unwrap();
|
||||
let backend = client.backend().as_in_memory();
|
||||
let longest_chain_select = test_client::client::LongestChain::new(
|
||||
Arc::new(backend),
|
||||
client.import_lock());
|
||||
|
||||
assert_eq!(None, client.best_containing(uninserted_block.hash().clone(), None).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
uninserted_block.hash().clone(), None).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1881,7 +1952,7 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn best_containing_with_single_chain_3_blocks() {
|
||||
fn best_containing_on_longest_chain_with_single_chain_3_blocks() {
|
||||
// block tree:
|
||||
// G -> A1 -> A2
|
||||
|
||||
@@ -1897,18 +1968,20 @@ pub(crate) mod tests {
|
||||
|
||||
let genesis_hash = client.info().unwrap().chain.genesis_hash;
|
||||
|
||||
assert_eq!(a2.hash(), client.best_containing(genesis_hash, None).unwrap().unwrap());
|
||||
assert_eq!(a2.hash(), client.best_containing(a1.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(a2.hash(), client.best_containing(a2.hash(), None).unwrap().unwrap());
|
||||
let longest_chain_select = test_client::client::LongestChain::new(
|
||||
Arc::new(client.backend().as_in_memory()),
|
||||
client.import_lock());
|
||||
|
||||
assert_eq!(a2.hash(), longest_chain_select.finality_target(
|
||||
genesis_hash, None).unwrap().unwrap());
|
||||
assert_eq!(a2.hash(), longest_chain_select.finality_target(
|
||||
a1.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(a2.hash(), longest_chain_select.finality_target(
|
||||
a2.hash(), None).unwrap().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn best_containing_with_multiple_forks() {
|
||||
// NOTE: we use the version of the trait from `test_client`
|
||||
// because that is actually different than the version linked to
|
||||
// in the test facade crate.
|
||||
use test_client::blockchain::Backend as BlockchainBackendT;
|
||||
|
||||
fn best_containing_on_longest_chain_with_multiple_forks() {
|
||||
// block tree:
|
||||
// G -> A1 -> A2 -> A3 -> A4 -> A5
|
||||
// A1 -> B2 -> B3 -> B4
|
||||
@@ -1983,7 +2056,11 @@ pub(crate) mod tests {
|
||||
assert_eq!(client.info().unwrap().chain.best_hash, a5.hash());
|
||||
|
||||
let genesis_hash = client.info().unwrap().chain.genesis_hash;
|
||||
let leaves = BlockchainBackendT::leaves(client.backend().blockchain()).unwrap();
|
||||
let longest_chain_select = test_client::client::LongestChain::new(
|
||||
Arc::new(client.backend().as_in_memory()),
|
||||
client.import_lock());
|
||||
|
||||
let leaves = longest_chain_select.leaves().unwrap();
|
||||
|
||||
assert!(leaves.contains(&a5.hash()));
|
||||
assert!(leaves.contains(&b4.hash()));
|
||||
@@ -1993,131 +2070,208 @@ pub(crate) mod tests {
|
||||
|
||||
// search without restriction
|
||||
|
||||
assert_eq!(a5.hash(), client.best_containing(genesis_hash, None).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), client.best_containing(a1.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), client.best_containing(a2.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), client.best_containing(a3.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), client.best_containing(a4.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), client.best_containing(a5.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), longest_chain_select.finality_target(
|
||||
genesis_hash, None).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), longest_chain_select.finality_target(
|
||||
a1.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), longest_chain_select.finality_target(
|
||||
a2.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), longest_chain_select.finality_target(
|
||||
a3.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), longest_chain_select.finality_target(
|
||||
a4.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), longest_chain_select.finality_target(
|
||||
a5.hash(), None).unwrap().unwrap());
|
||||
|
||||
assert_eq!(b4.hash(), client.best_containing(b2.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), client.best_containing(b3.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), client.best_containing(b4.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), longest_chain_select.finality_target(
|
||||
b2.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), longest_chain_select.finality_target(
|
||||
b3.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), longest_chain_select.finality_target(
|
||||
b4.hash(), None).unwrap().unwrap());
|
||||
|
||||
assert_eq!(c3.hash(), client.best_containing(c3.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(c3.hash(), longest_chain_select.finality_target(
|
||||
c3.hash(), None).unwrap().unwrap());
|
||||
|
||||
assert_eq!(d2.hash(), client.best_containing(d2.hash(), None).unwrap().unwrap());
|
||||
assert_eq!(d2.hash(), longest_chain_select.finality_target(
|
||||
d2.hash(), None).unwrap().unwrap());
|
||||
|
||||
|
||||
// search only blocks with number <= 5. equivalent to without restriction for this scenario
|
||||
|
||||
assert_eq!(a5.hash(), client.best_containing(genesis_hash, Some(5)).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), client.best_containing(a1.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), client.best_containing(a2.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), client.best_containing(a3.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), client.best_containing(a4.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), client.best_containing(a5.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), longest_chain_select.finality_target(
|
||||
genesis_hash, Some(5)).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), longest_chain_select.finality_target(
|
||||
a1.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), longest_chain_select.finality_target(
|
||||
a2.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), longest_chain_select.finality_target(
|
||||
a3.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), longest_chain_select.finality_target(
|
||||
a4.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(a5.hash(), longest_chain_select.finality_target(
|
||||
a5.hash(), Some(5)).unwrap().unwrap());
|
||||
|
||||
assert_eq!(b4.hash(), client.best_containing(b2.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), client.best_containing(b3.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), client.best_containing(b4.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), longest_chain_select.finality_target(
|
||||
b2.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), longest_chain_select.finality_target(
|
||||
b3.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), longest_chain_select.finality_target(
|
||||
b4.hash(), Some(5)).unwrap().unwrap());
|
||||
|
||||
assert_eq!(c3.hash(), client.best_containing(c3.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(c3.hash(), longest_chain_select.finality_target(
|
||||
c3.hash(), Some(5)).unwrap().unwrap());
|
||||
|
||||
assert_eq!(d2.hash(), client.best_containing(d2.hash(), Some(5)).unwrap().unwrap());
|
||||
assert_eq!(d2.hash(), longest_chain_select.finality_target(
|
||||
d2.hash(), Some(5)).unwrap().unwrap());
|
||||
|
||||
|
||||
// search only blocks with number <= 4
|
||||
|
||||
assert_eq!(a4.hash(), client.best_containing(genesis_hash, Some(4)).unwrap().unwrap());
|
||||
assert_eq!(a4.hash(), client.best_containing(a1.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(a4.hash(), client.best_containing(a2.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(a4.hash(), client.best_containing(a3.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(a4.hash(), client.best_containing(a4.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(None, client.best_containing(a5.hash(), Some(4)).unwrap());
|
||||
assert_eq!(a4.hash(), longest_chain_select.finality_target(
|
||||
genesis_hash, Some(4)).unwrap().unwrap());
|
||||
assert_eq!(a4.hash(), longest_chain_select.finality_target(
|
||||
a1.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(a4.hash(), longest_chain_select.finality_target(
|
||||
a2.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(a4.hash(), longest_chain_select.finality_target(
|
||||
a3.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(a4.hash(), longest_chain_select.finality_target(
|
||||
a4.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a5.hash(), Some(4)).unwrap());
|
||||
|
||||
assert_eq!(b4.hash(), client.best_containing(b2.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), client.best_containing(b3.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), client.best_containing(b4.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), longest_chain_select.finality_target(
|
||||
b2.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), longest_chain_select.finality_target(
|
||||
b3.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(b4.hash(), longest_chain_select.finality_target(
|
||||
b4.hash(), Some(4)).unwrap().unwrap());
|
||||
|
||||
assert_eq!(c3.hash(), client.best_containing(c3.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(c3.hash(), longest_chain_select.finality_target(
|
||||
c3.hash(), Some(4)).unwrap().unwrap());
|
||||
|
||||
assert_eq!(d2.hash(), client.best_containing(d2.hash(), Some(4)).unwrap().unwrap());
|
||||
assert_eq!(d2.hash(), longest_chain_select.finality_target(
|
||||
d2.hash(), Some(4)).unwrap().unwrap());
|
||||
|
||||
|
||||
// search only blocks with number <= 3
|
||||
|
||||
assert_eq!(a3.hash(), client.best_containing(genesis_hash, Some(3)).unwrap().unwrap());
|
||||
assert_eq!(a3.hash(), client.best_containing(a1.hash(), Some(3)).unwrap().unwrap());
|
||||
assert_eq!(a3.hash(), client.best_containing(a2.hash(), Some(3)).unwrap().unwrap());
|
||||
assert_eq!(a3.hash(), client.best_containing(a3.hash(), Some(3)).unwrap().unwrap());
|
||||
assert_eq!(None, client.best_containing(a4.hash(), Some(3)).unwrap());
|
||||
assert_eq!(None, client.best_containing(a5.hash(), Some(3)).unwrap());
|
||||
assert_eq!(a3.hash(), longest_chain_select.finality_target(
|
||||
genesis_hash, Some(3)).unwrap().unwrap());
|
||||
assert_eq!(a3.hash(), longest_chain_select.finality_target(
|
||||
a1.hash(), Some(3)).unwrap().unwrap());
|
||||
assert_eq!(a3.hash(), longest_chain_select.finality_target(
|
||||
a2.hash(), Some(3)).unwrap().unwrap());
|
||||
assert_eq!(a3.hash(), longest_chain_select.finality_target(
|
||||
a3.hash(), Some(3)).unwrap().unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a4.hash(), Some(3)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a5.hash(), Some(3)).unwrap());
|
||||
|
||||
assert_eq!(b3.hash(), client.best_containing(b2.hash(), Some(3)).unwrap().unwrap());
|
||||
assert_eq!(b3.hash(), client.best_containing(b3.hash(), Some(3)).unwrap().unwrap());
|
||||
assert_eq!(None, client.best_containing(b4.hash(), Some(3)).unwrap());
|
||||
assert_eq!(b3.hash(), longest_chain_select.finality_target(
|
||||
b2.hash(), Some(3)).unwrap().unwrap());
|
||||
assert_eq!(b3.hash(), longest_chain_select.finality_target(
|
||||
b3.hash(), Some(3)).unwrap().unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
b4.hash(), Some(3)).unwrap());
|
||||
|
||||
assert_eq!(c3.hash(), client.best_containing(c3.hash(), Some(3)).unwrap().unwrap());
|
||||
assert_eq!(c3.hash(), longest_chain_select.finality_target(
|
||||
c3.hash(), Some(3)).unwrap().unwrap());
|
||||
|
||||
assert_eq!(d2.hash(), client.best_containing(d2.hash(), Some(3)).unwrap().unwrap());
|
||||
assert_eq!(d2.hash(), longest_chain_select.finality_target(
|
||||
d2.hash(), Some(3)).unwrap().unwrap());
|
||||
|
||||
|
||||
// search only blocks with number <= 2
|
||||
|
||||
assert_eq!(a2.hash(), client.best_containing(genesis_hash, Some(2)).unwrap().unwrap());
|
||||
assert_eq!(a2.hash(), client.best_containing(a1.hash(), Some(2)).unwrap().unwrap());
|
||||
assert_eq!(a2.hash(), client.best_containing(a2.hash(), Some(2)).unwrap().unwrap());
|
||||
assert_eq!(None, client.best_containing(a3.hash(), Some(2)).unwrap());
|
||||
assert_eq!(None, client.best_containing(a4.hash(), Some(2)).unwrap());
|
||||
assert_eq!(None, client.best_containing(a5.hash(), Some(2)).unwrap());
|
||||
assert_eq!(a2.hash(), longest_chain_select.finality_target(
|
||||
genesis_hash, Some(2)).unwrap().unwrap());
|
||||
assert_eq!(a2.hash(), longest_chain_select.finality_target(
|
||||
a1.hash(), Some(2)).unwrap().unwrap());
|
||||
assert_eq!(a2.hash(), longest_chain_select.finality_target(
|
||||
a2.hash(), Some(2)).unwrap().unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a3.hash(), Some(2)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a4.hash(), Some(2)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a5.hash(), Some(2)).unwrap());
|
||||
|
||||
assert_eq!(b2.hash(), client.best_containing(b2.hash(), Some(2)).unwrap().unwrap());
|
||||
assert_eq!(None, client.best_containing(b3.hash(), Some(2)).unwrap());
|
||||
assert_eq!(None, client.best_containing(b4.hash(), Some(2)).unwrap());
|
||||
assert_eq!(b2.hash(), longest_chain_select.finality_target(
|
||||
b2.hash(), Some(2)).unwrap().unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
b3.hash(), Some(2)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
b4.hash(), Some(2)).unwrap());
|
||||
|
||||
assert_eq!(None, client.best_containing(c3.hash(), Some(2)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
c3.hash(), Some(2)).unwrap());
|
||||
|
||||
assert_eq!(d2.hash(), client.best_containing(d2.hash(), Some(2)).unwrap().unwrap());
|
||||
assert_eq!(d2.hash(), longest_chain_select.finality_target(
|
||||
d2.hash(), Some(2)).unwrap().unwrap());
|
||||
|
||||
|
||||
// search only blocks with number <= 1
|
||||
|
||||
assert_eq!(a1.hash(), client.best_containing(genesis_hash, Some(1)).unwrap().unwrap());
|
||||
assert_eq!(a1.hash(), client.best_containing(a1.hash(), Some(1)).unwrap().unwrap());
|
||||
assert_eq!(None, client.best_containing(a2.hash(), Some(1)).unwrap());
|
||||
assert_eq!(None, client.best_containing(a3.hash(), Some(1)).unwrap());
|
||||
assert_eq!(None, client.best_containing(a4.hash(), Some(1)).unwrap());
|
||||
assert_eq!(None, client.best_containing(a5.hash(), Some(1)).unwrap());
|
||||
assert_eq!(a1.hash(), longest_chain_select.finality_target(
|
||||
genesis_hash, Some(1)).unwrap().unwrap());
|
||||
assert_eq!(a1.hash(), longest_chain_select.finality_target(
|
||||
a1.hash(), Some(1)).unwrap().unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a2.hash(), Some(1)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a3.hash(), Some(1)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a4.hash(), Some(1)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a5.hash(), Some(1)).unwrap());
|
||||
|
||||
assert_eq!(None, client.best_containing(b2.hash(), Some(1)).unwrap());
|
||||
assert_eq!(None, client.best_containing(b3.hash(), Some(1)).unwrap());
|
||||
assert_eq!(None, client.best_containing(b4.hash(), Some(1)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
b2.hash(), Some(1)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
b3.hash(), Some(1)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
b4.hash(), Some(1)).unwrap());
|
||||
|
||||
assert_eq!(None, client.best_containing(c3.hash(), Some(1)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
c3.hash(), Some(1)).unwrap());
|
||||
|
||||
assert_eq!(None, client.best_containing(d2.hash(), Some(1)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
d2.hash(), Some(1)).unwrap());
|
||||
|
||||
// search only blocks with number <= 0
|
||||
|
||||
assert_eq!(genesis_hash, client.best_containing(genesis_hash, Some(0)).unwrap().unwrap());
|
||||
assert_eq!(None, client.best_containing(a1.hash(), Some(0)).unwrap());
|
||||
assert_eq!(None, client.best_containing(a2.hash(), Some(0)).unwrap());
|
||||
assert_eq!(None, client.best_containing(a3.hash(), Some(0)).unwrap());
|
||||
assert_eq!(None, client.best_containing(a4.hash(), Some(0)).unwrap());
|
||||
assert_eq!(None, client.best_containing(a5.hash(), Some(0)).unwrap());
|
||||
assert_eq!(genesis_hash, longest_chain_select.finality_target(
|
||||
genesis_hash, Some(0)).unwrap().unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a1.hash(), Some(0)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a2.hash(), Some(0)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a3.hash(), Some(0)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a4.hash(), Some(0)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
a5.hash(), Some(0)).unwrap());
|
||||
|
||||
assert_eq!(None, client.best_containing(b2.hash(), Some(0)).unwrap());
|
||||
assert_eq!(None, client.best_containing(b3.hash(), Some(0)).unwrap());
|
||||
assert_eq!(None, client.best_containing(b4.hash(), Some(0)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
b2.hash(), Some(0)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
b3.hash(), Some(0)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
b4.hash(), Some(0)).unwrap());
|
||||
|
||||
assert_eq!(None, client.best_containing(c3.hash().clone(), Some(0)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
c3.hash().clone(), Some(0)).unwrap());
|
||||
|
||||
assert_eq!(None, client.best_containing(d2.hash().clone(), Some(0)).unwrap());
|
||||
assert_eq!(None, longest_chain_select.finality_target(
|
||||
d2.hash().clone(), Some(0)).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn best_containing_with_max_depth_higher_than_best() {
|
||||
fn best_containing_on_longest_chain_with_max_depth_higher_than_best() {
|
||||
// block tree:
|
||||
// G -> A1 -> A2
|
||||
|
||||
@@ -2132,8 +2286,13 @@ pub(crate) mod tests {
|
||||
client.import(BlockOrigin::Own, a2.clone()).unwrap();
|
||||
|
||||
let genesis_hash = client.info().unwrap().chain.genesis_hash;
|
||||
let longest_chain_select = test_client::client::LongestChain::new(
|
||||
Arc::new(client.backend().as_in_memory()),
|
||||
client.import_lock()
|
||||
);
|
||||
|
||||
assert_eq!(a2.hash(), client.best_containing(genesis_hash, Some(10)).unwrap().unwrap());
|
||||
assert_eq!(a2.hash(), longest_chain_select.finality_target(
|
||||
genesis_hash, Some(10)).unwrap().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -58,7 +58,8 @@ pub use crate::client::{
|
||||
new_with_backend,
|
||||
new_in_mem,
|
||||
BlockBody, BlockStatus, ImportNotifications, FinalityNotifications, BlockchainEvents,
|
||||
BlockImportNotification, Client, ClientInfo, ChainHead, ExecutionStrategies,
|
||||
BlockImportNotification, Client, ClientInfo, ExecutionStrategies,
|
||||
LongestChain
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
pub use crate::notifications::{StorageEventStream, StorageChangeSet};
|
||||
|
||||
@@ -25,18 +25,16 @@
|
||||
//!
|
||||
//! Blocks from future steps will be either deferred or rejected depending on how
|
||||
//! far in the future they are.
|
||||
#![deny(warnings)]
|
||||
#![forbid(missing_docs, unsafe_code)]
|
||||
use std::{sync::Arc, time::Duration, thread, marker::PhantomData, hash::Hash, fmt::Debug};
|
||||
|
||||
use parity_codec::{Encode, Decode};
|
||||
use consensus_common::{self, Authorities, BlockImport, Environment, Proposer,
|
||||
ForkChoiceStrategy, ImportBlock, BlockOrigin, Error as ConsensusError,
|
||||
SelectChain, well_known_cache_keys
|
||||
};
|
||||
use consensus_common::well_known_cache_keys;
|
||||
use consensus_common::import_queue::{Verifier, BasicQueue, SharedBlockImport, SharedJustificationImport};
|
||||
use client::{
|
||||
ChainHead,
|
||||
block_builder::api::BlockBuilder as BlockBuilderApi,
|
||||
blockchain::ProvideCache,
|
||||
runtime_api::{ApiExt, Core as CoreApi},
|
||||
@@ -182,10 +180,11 @@ impl SlotCompatible for AuraSlotCompatible {
|
||||
|
||||
/// Start the aura worker in a separate thread.
|
||||
#[deprecated(since = "1.1", note = "Please spawn a thread manually")]
|
||||
pub fn start_aura_thread<B, C, E, I, P, SO, Error, OnExit>(
|
||||
pub fn start_aura_thread<B, C, SC, E, I, P, SO, Error, OnExit>(
|
||||
slot_duration: SlotDuration,
|
||||
local_key: Arc<P>,
|
||||
client: Arc<C>,
|
||||
select_chain: SC,
|
||||
block_import: Arc<I>,
|
||||
env: Arc<E>,
|
||||
sync_oracle: SO,
|
||||
@@ -194,8 +193,9 @@ pub fn start_aura_thread<B, C, E, I, P, SO, Error, OnExit>(
|
||||
force_authoring: bool,
|
||||
) -> Result<(), consensus_common::Error> where
|
||||
B: Block + 'static,
|
||||
C: ChainHead<B> + ProvideRuntimeApi + ProvideCache<B> + Send + Sync + 'static,
|
||||
C: ProvideRuntimeApi + ProvideCache<B> + Send + Sync + 'static,
|
||||
C::Api: AuthoritiesApi<B>,
|
||||
SC: SelectChain<B> + Clone + 'static,
|
||||
E: Environment<B, Error=Error> + Send + Sync + 'static,
|
||||
E::Proposer: Proposer<B, Error=Error> + Send + 'static,
|
||||
<<E::Proposer as Proposer<B>>::Create as IntoFuture>::Future: Send + 'static,
|
||||
@@ -222,7 +222,7 @@ pub fn start_aura_thread<B, C, E, I, P, SO, Error, OnExit>(
|
||||
#[allow(deprecated)] // The function we are in is also deprecated.
|
||||
slots::start_slot_worker_thread::<_, _, _, _, AuraSlotCompatible, u64, _>(
|
||||
slot_duration.0,
|
||||
client,
|
||||
select_chain,
|
||||
Arc::new(worker),
|
||||
sync_oracle,
|
||||
on_exit,
|
||||
@@ -231,10 +231,11 @@ pub fn start_aura_thread<B, C, E, I, P, SO, Error, OnExit>(
|
||||
}
|
||||
|
||||
/// Start the aura worker. The returned future should be run in a tokio runtime.
|
||||
pub fn start_aura<B, C, E, I, P, SO, Error, OnExit>(
|
||||
pub fn start_aura<B, C, SC, E, I, P, SO, Error, OnExit>(
|
||||
slot_duration: SlotDuration,
|
||||
local_key: Arc<P>,
|
||||
client: Arc<C>,
|
||||
select_chain: SC,
|
||||
block_import: Arc<I>,
|
||||
env: Arc<E>,
|
||||
sync_oracle: SO,
|
||||
@@ -243,8 +244,9 @@ pub fn start_aura<B, C, E, I, P, SO, Error, OnExit>(
|
||||
force_authoring: bool,
|
||||
) -> Result<impl Future<Item=(), Error=()>, consensus_common::Error> where
|
||||
B: Block,
|
||||
C: ChainHead<B> + ProvideRuntimeApi + ProvideCache<B>,
|
||||
C: ProvideRuntimeApi + ProvideCache<B>,
|
||||
C::Api: AuthoritiesApi<B>,
|
||||
SC: SelectChain<B> + Clone,
|
||||
E: Environment<B, Error=Error>,
|
||||
E::Proposer: Proposer<B, Error=Error>,
|
||||
<<E::Proposer as Proposer<B>>::Create as IntoFuture>::Future: Send + 'static,
|
||||
@@ -268,7 +270,7 @@ pub fn start_aura<B, C, E, I, P, SO, Error, OnExit>(
|
||||
};
|
||||
slots::start_slot_worker::<_, _, _, _, _, AuraSlotCompatible, _>(
|
||||
slot_duration.0,
|
||||
client,
|
||||
select_chain,
|
||||
Arc::new(worker),
|
||||
sync_oracle,
|
||||
on_exit,
|
||||
@@ -804,7 +806,7 @@ mod tests {
|
||||
use tokio::runtime::current_thread;
|
||||
use keyring::sr25519::Keyring;
|
||||
use primitives::sr25519;
|
||||
use client::BlockchainEvents;
|
||||
use client::{LongestChain, BlockchainEvents};
|
||||
use test_client;
|
||||
|
||||
type Error = client::error::Error;
|
||||
@@ -916,6 +918,10 @@ mod tests {
|
||||
let mut runtime = current_thread::Runtime::new().unwrap();
|
||||
for (peer_id, key) in peers {
|
||||
let client = net.lock().peer(*peer_id).client().clone();
|
||||
let select_chain = LongestChain::new(
|
||||
client.backend().clone(),
|
||||
client.import_lock().clone(),
|
||||
);
|
||||
let environ = Arc::new(DummyFactory(client.clone()));
|
||||
import_notifications.push(
|
||||
client.import_notification_stream()
|
||||
@@ -931,10 +937,11 @@ mod tests {
|
||||
&inherent_data_providers, slot_duration.get()
|
||||
).expect("Registers aura inherent data provider");
|
||||
|
||||
let aura = start_aura::<_, _, _, _, sr25519::Pair, _, _, _>(
|
||||
let aura = start_aura::<_, _, _, _, _, sr25519::Pair, _, _, _>(
|
||||
slot_duration,
|
||||
Arc::new(key.clone().into()),
|
||||
client.clone(),
|
||||
select_chain,
|
||||
client,
|
||||
environ.clone(),
|
||||
DummyOracle,
|
||||
|
||||
@@ -58,10 +58,9 @@ use srml_babe::{
|
||||
BabeInherentData,
|
||||
timestamp::{TimestampInherentData, InherentType as TimestampInherent}
|
||||
};
|
||||
use consensus_common::well_known_cache_keys;
|
||||
use consensus_common::{SelectChain, well_known_cache_keys};
|
||||
use consensus_common::import_queue::{Verifier, BasicQueue};
|
||||
use client::{
|
||||
ChainHead,
|
||||
block_builder::api::BlockBuilder as BlockBuilderApi,
|
||||
blockchain::ProvideCache,
|
||||
runtime_api::ApiExt,
|
||||
@@ -249,7 +248,7 @@ impl SlotCompatible for BabeSlotCompatible {
|
||||
}
|
||||
|
||||
/// Parameters for BABE.
|
||||
pub struct BabeParams<C, E, I, SO, OnExit> {
|
||||
pub struct BabeParams<C, E, I, SO, SC, OnExit> {
|
||||
|
||||
/// The configuration for BABE. Includes the slot duration, threshold, and
|
||||
/// other parameters.
|
||||
@@ -261,6 +260,9 @@ pub struct BabeParams<C, E, I, SO, OnExit> {
|
||||
/// The client to use
|
||||
pub client: Arc<C>,
|
||||
|
||||
/// The SelectChain Strategy
|
||||
pub select_chain: SC,
|
||||
|
||||
/// A block importer
|
||||
pub block_import: Arc<I>,
|
||||
|
||||
@@ -281,28 +283,30 @@ pub struct BabeParams<C, E, I, SO, OnExit> {
|
||||
}
|
||||
|
||||
/// Start the babe worker. The returned future should be run in a tokio runtime.
|
||||
pub fn start_babe<B, C, E, I, SO, Error, OnExit>(BabeParams {
|
||||
pub fn start_babe<B, C, E, I, SO, SC, Error, OnExit>(BabeParams {
|
||||
config,
|
||||
local_key,
|
||||
client,
|
||||
select_chain,
|
||||
block_import,
|
||||
env,
|
||||
sync_oracle,
|
||||
on_exit,
|
||||
inherent_data_providers,
|
||||
force_authoring,
|
||||
}: BabeParams<C, E, I, SO, OnExit>) -> Result<
|
||||
}: BabeParams<C, E, I, SO, SC, OnExit>) -> Result<
|
||||
impl Future<Item=(), Error=()>,
|
||||
consensus_common::Error,
|
||||
> where
|
||||
B: Block,
|
||||
C: ChainHead<B> + ProvideRuntimeApi + ProvideCache<B>,
|
||||
C: ProvideRuntimeApi + ProvideCache<B>,
|
||||
C::Api: AuthoritiesApi<B>,
|
||||
E: Environment<B, Error=Error>,
|
||||
E::Proposer: Proposer<B, Error=Error>,
|
||||
<<E::Proposer as Proposer<B>>::Create as IntoFuture>::Future: Send + 'static,
|
||||
I: BlockImport<B> + Send + Sync + 'static,
|
||||
SO: SyncOracle + Send + Sync + Clone,
|
||||
SC: SelectChain<B>,
|
||||
DigestItemFor<B>: CompatibleDigestItem + DigestItem<AuthorityId=Public>,
|
||||
Error: ::std::error::Error + Send + From<::consensus_common::Error> + From<I::Error> + 'static,
|
||||
OnExit: Future<Item=(), Error=()>,
|
||||
@@ -319,7 +323,7 @@ pub fn start_babe<B, C, E, I, SO, Error, OnExit>(BabeParams {
|
||||
};
|
||||
slots::start_slot_worker::<_, _, _, _, _, BabeSlotCompatible, _>(
|
||||
config.0,
|
||||
client,
|
||||
select_chain,
|
||||
Arc::new(worker),
|
||||
sync_oracle,
|
||||
on_exit,
|
||||
@@ -847,10 +851,13 @@ fn claim_slot(
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(dead_code, unused_imports)]
|
||||
#[allow(dead_code, unused_imports, deprecated)]
|
||||
// FIXME #2532: need to allow deprecated until refactor is done https://github.com/paritytech/substrate/issues/2532
|
||||
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use client::LongestChain;
|
||||
use consensus_common::NoNetwork as DummyOracle;
|
||||
use network::test::*;
|
||||
use network::test::{Block as TestBlock, PeersClient};
|
||||
@@ -1014,6 +1021,7 @@ mod tests {
|
||||
config,
|
||||
local_key: Arc::new(key.clone().into()),
|
||||
block_import: client.clone(),
|
||||
select_chain: LongestChain::new(client.backend().clone(), client.import_lock().clone()),
|
||||
client,
|
||||
env: environ.clone(),
|
||||
sync_oracle: DummyOracle,
|
||||
|
||||
@@ -105,5 +105,11 @@ error_chain! {
|
||||
description("Import failed"),
|
||||
display("Import failed: {}", reason),
|
||||
}
|
||||
|
||||
/// Error from the client while importing
|
||||
ChainLookup(reason: String) {
|
||||
description("Looking up chain failed"),
|
||||
display("Chain lookup failed: {}", reason),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ pub use inherents::InherentData;
|
||||
pub mod offline_tracker;
|
||||
pub mod error;
|
||||
mod block_import;
|
||||
mod select_chain;
|
||||
pub mod import_queue;
|
||||
pub mod evaluation;
|
||||
|
||||
@@ -50,6 +51,7 @@ pub use self::error::{Error, ErrorKind};
|
||||
pub use block_import::{
|
||||
BlockImport, BlockOrigin, ForkChoiceStrategy, ImportedAux, ImportBlock, ImportResult, JustificationImport,
|
||||
};
|
||||
pub use select_chain::SelectChain;
|
||||
|
||||
/// Trait for getting the authorities at a given block.
|
||||
pub trait Authorities<B: Block> {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate Consensus Common.
|
||||
|
||||
// Substrate Demo 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.
|
||||
|
||||
// Substrate Consensus Common 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 Substrate Consensus Common. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::error::Error;
|
||||
use runtime_primitives::traits::{Block as BlockT, NumberFor};
|
||||
|
||||
|
||||
/// The SelectChain trait defines the strategy upon which the head is chosen
|
||||
/// if multiple forks are present for an opaque definition of "best" in the
|
||||
/// specific chain build.
|
||||
///
|
||||
/// The Strategy can be customised for the two use cases of authoring new blocks
|
||||
/// upon the best chain or which fork to finalise. Unless implemented differently
|
||||
/// by default finalisation methods fall back to use authoring, so as a minimum
|
||||
/// `_authoring`-functions must be implemented.
|
||||
///
|
||||
/// Any particular user must make explicit, however, whether they intend to finalise
|
||||
/// or author through the using the right function call, as these might differ in
|
||||
/// some implementations.
|
||||
///
|
||||
/// Non-deterministicly finalising chains may only use the `_authoring` functions.
|
||||
pub trait SelectChain<Block: BlockT>: Sync + Send + Clone {
|
||||
|
||||
/// Get all leaves of the chain: block hashes that have no children currently.
|
||||
/// Leaves that can never be finalized will not be returned.
|
||||
fn leaves(&self) -> Result<Vec<<Block as BlockT>::Hash>, Error>;
|
||||
|
||||
/// Among those `leaves` deterministically pick one chain as the generally
|
||||
/// best chain to author new blocks upon and probably finalize.
|
||||
fn best_chain(&self) -> Result<<Block as BlockT>::Header, Error>;
|
||||
|
||||
/// Get the best ancestor of `target_hash` that we should attempt
|
||||
/// to finalize next.
|
||||
fn finality_target(
|
||||
&self,
|
||||
target_hash: <Block as BlockT>::Hash,
|
||||
_maybe_max_number: Option<NumberFor<Block>>
|
||||
) -> Result<Option<<Block as BlockT>::Hash>, Error> {
|
||||
Ok(Some(target_hash))
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::sync::Arc;
|
||||
|
||||
use client::{BlockchainEvents, ChainHead, BlockBody};
|
||||
use client::{BlockchainEvents, BlockBody};
|
||||
use futures::prelude::*;
|
||||
use transaction_pool::txpool::{Pool as TransactionPool, ChainApi as PoolChainApi};
|
||||
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, BlockNumberToHash};
|
||||
@@ -33,7 +33,7 @@ use tokio::runtime::current_thread::Runtime as LocalRuntime;
|
||||
use tokio::timer::Interval;
|
||||
|
||||
use parking_lot::RwLock;
|
||||
use consensus::offline_tracker::OfflineTracker;
|
||||
use consensus::{self, offline_tracker::OfflineTracker};
|
||||
|
||||
use super::{Network, ProposerFactory, AuthoringApi};
|
||||
use {consensus, primitives, ed25519, error, BftService, LocalProposer};
|
||||
@@ -87,9 +87,9 @@ impl Service {
|
||||
A: AuthoringApi + BlockNumberToHash + 'static,
|
||||
P: PoolChainApi<Block = <A as AuthoringApi>::Block> + 'static,
|
||||
C: BlockchainEvents<<A as AuthoringApi>::Block>
|
||||
+ ChainHead<<A as AuthoringApi>::Block>
|
||||
+ BlockBody<<A as AuthoringApi>::Block>,
|
||||
C: consensus::BlockImport<<A as AuthoringApi>::Block>
|
||||
+ BlockBody<<A as AuthoringApi>::Block>
|
||||
+ consensus::SelectChain<<A as AuthoringApi>::Block>
|
||||
+ consensus::BlockImport<<A as AuthoringApi>::Block>
|
||||
+ consensus::Authorities<<A as AuthoringApi>::Block> + Send + Sync + 'static,
|
||||
primitives::H256: From<<<A as AuthoringApi>::Block as BlockT>::Hash>,
|
||||
<<A as AuthoringApi>::Block as BlockT>::Hash: PartialEq<primitives::H256> + PartialEq,
|
||||
|
||||
@@ -26,9 +26,8 @@ mod slots;
|
||||
|
||||
pub use slots::{slot_now, SlotInfo, Slots};
|
||||
|
||||
use client::ChainHead;
|
||||
use codec::{Decode, Encode};
|
||||
use consensus_common::SyncOracle;
|
||||
use consensus_common::{SyncOracle, SelectChain};
|
||||
use futures::prelude::*;
|
||||
use futures::{
|
||||
future::{self, Either},
|
||||
@@ -73,7 +72,7 @@ pub fn inherent_to_common_error(err: inherents::RuntimeString) -> consensus_comm
|
||||
#[deprecated(since = "1.1", note = "Please spawn a thread manually")]
|
||||
pub fn start_slot_worker_thread<B, C, W, SO, SC, T, OnExit>(
|
||||
slot_duration: SlotDuration<T>,
|
||||
client: Arc<C>,
|
||||
select_chain: C,
|
||||
worker: Arc<W>,
|
||||
sync_oracle: SO,
|
||||
on_exit: OnExit,
|
||||
@@ -81,7 +80,7 @@ pub fn start_slot_worker_thread<B, C, W, SO, SC, T, OnExit>(
|
||||
) -> Result<(), consensus_common::Error>
|
||||
where
|
||||
B: Block + 'static,
|
||||
C: ChainHead<B> + Send + Sync + 'static,
|
||||
C: SelectChain<B> + Clone + 'static,
|
||||
W: SlotWorker<B> + Send + Sync + 'static,
|
||||
SO: SyncOracle + Send + Clone + 'static,
|
||||
SC: SlotCompatible + 'static,
|
||||
@@ -101,9 +100,9 @@ where
|
||||
}
|
||||
};
|
||||
|
||||
let slot_worker_future = match start_slot_worker::<_, _, _, _, _, SC, _>(
|
||||
let slot_worker_future = match start_slot_worker::<_, _, _, T, _, SC, _>(
|
||||
slot_duration.clone(),
|
||||
client,
|
||||
select_chain,
|
||||
worker,
|
||||
sync_oracle,
|
||||
on_exit,
|
||||
@@ -134,7 +133,7 @@ where
|
||||
/// Start a new slot worker.
|
||||
pub fn start_slot_worker<B, C, W, T, SO, SC, OnExit>(
|
||||
slot_duration: SlotDuration<T>,
|
||||
client: Arc<C>,
|
||||
client: C,
|
||||
worker: Arc<W>,
|
||||
sync_oracle: SO,
|
||||
on_exit: OnExit,
|
||||
@@ -142,7 +141,7 @@ pub fn start_slot_worker<B, C, W, T, SO, SC, OnExit>(
|
||||
) -> Result<impl Future<Item = (), Error = ()>, consensus_common::Error>
|
||||
where
|
||||
B: Block,
|
||||
C: ChainHead<B>,
|
||||
C: SelectChain<B> + Clone,
|
||||
W: SlotWorker<B>,
|
||||
SO: SyncOracle + Send + Clone,
|
||||
SC: SlotCompatible,
|
||||
@@ -173,7 +172,7 @@ where
|
||||
}
|
||||
|
||||
let slot_num = slot_info.number;
|
||||
let chain_head = match client.best_block_header() {
|
||||
let chain_head = match client.best_chain() {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
warn!(target: "slots", "Unable to author block in slot {}. \
|
||||
|
||||
@@ -44,6 +44,8 @@ use crate::{
|
||||
PrimaryPropose, SignedMessage, NewAuthoritySet, VoterCommand,
|
||||
};
|
||||
|
||||
use consensus_common::SelectChain;
|
||||
|
||||
use crate::authorities::SharedAuthoritySet;
|
||||
use crate::consensus_changes::SharedConsensusChanges;
|
||||
use crate::justification::GrandpaJustification;
|
||||
@@ -262,8 +264,9 @@ impl<Block: BlockT> SharedVoterSetState<Block> {
|
||||
}
|
||||
|
||||
/// The environment we run GRANDPA in.
|
||||
pub(crate) struct Environment<B, E, Block: BlockT, N: Network<Block>, RA> {
|
||||
pub(crate) struct Environment<B, E, Block: BlockT, N: Network<Block>, RA, SC> {
|
||||
pub(crate) inner: Arc<Client<B, E, Block, RA>>,
|
||||
pub(crate) select_chain: SC,
|
||||
pub(crate) voters: Arc<VoterSet<AuthorityId>>,
|
||||
pub(crate) config: Config,
|
||||
pub(crate) authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||
@@ -273,7 +276,7 @@ pub(crate) struct Environment<B, E, Block: BlockT, N: Network<Block>, RA> {
|
||||
pub(crate) voter_set_state: SharedVoterSetState<Block>,
|
||||
}
|
||||
|
||||
impl<B, E, Block: BlockT, N: Network<Block>, RA> Environment<B, E, Block, N, RA> {
|
||||
impl<B, E, Block: BlockT, N: Network<Block>, RA, SC> Environment<B, E, Block, N, RA, SC> {
|
||||
/// Updates the voter set state using the given closure. The write lock is
|
||||
/// held during evaluation of the closure and the environment's voter set
|
||||
/// state is set to its result if successful.
|
||||
@@ -289,12 +292,16 @@ impl<B, E, Block: BlockT, N: Network<Block>, RA> Environment<B, E, Block, N, RA>
|
||||
}
|
||||
}
|
||||
|
||||
impl<Block: BlockT<Hash=H256>, B, E, N, RA> grandpa::Chain<Block::Hash, NumberFor<Block>> for Environment<B, E, Block, N, RA> where
|
||||
impl<Block: BlockT<Hash=H256>, B, E, N, RA, SC>
|
||||
grandpa::Chain<Block::Hash, NumberFor<Block>>
|
||||
for Environment<B, E, Block, N, RA, SC>
|
||||
where
|
||||
Block: 'static,
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static,
|
||||
N: Network<Block> + 'static,
|
||||
N::In: 'static,
|
||||
SC: SelectChain<Block> + 'static,
|
||||
NumberFor<Block>: BlockNumberOps,
|
||||
{
|
||||
fn ancestry(&self, base: Block::Hash, block: Block::Hash) -> Result<Vec<Block::Hash>, GrandpaError> {
|
||||
@@ -317,7 +324,7 @@ impl<Block: BlockT<Hash=H256>, B, E, N, RA> grandpa::Chain<Block::Hash, NumberFo
|
||||
let limit = self.authority_set.current_limit();
|
||||
debug!(target: "afg", "Finding best chain containing block {:?} with number limit {:?}", block, limit);
|
||||
|
||||
match self.inner.best_containing(block, None) {
|
||||
match self.select_chain.finality_target(block, None) {
|
||||
Ok(Some(mut best_hash)) => {
|
||||
let base_header = self.inner.header(&BlockId::Hash(block)).ok()?
|
||||
.expect("Header known to exist after `best_containing` call; qed");
|
||||
@@ -376,6 +383,7 @@ impl<Block: BlockT<Hash=H256>, B, E, N, RA> grandpa::Chain<Block::Hash, NumberFo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub(crate) fn ancestry<B, Block: BlockT<Hash=H256>, E, RA>(
|
||||
client: &Client<B, E, Block, RA>,
|
||||
base: Block::Hash,
|
||||
@@ -411,13 +419,17 @@ pub(crate) fn ancestry<B, Block: BlockT<Hash=H256>, E, RA>(
|
||||
Ok(tree_route.retracted().iter().skip(1).map(|e| e.hash).collect())
|
||||
}
|
||||
|
||||
impl<B, E, Block: BlockT<Hash=H256>, N, RA> voter::Environment<Block::Hash, NumberFor<Block>> for Environment<B, E, Block, N, RA> where
|
||||
impl<B, E, Block: BlockT<Hash=H256>, N, RA, SC>
|
||||
voter::Environment<Block::Hash, NumberFor<Block>>
|
||||
for Environment<B, E, Block, N, RA, SC>
|
||||
where
|
||||
Block: 'static,
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static + Send + Sync,
|
||||
N: Network<Block> + 'static + Send,
|
||||
N::In: 'static + Send,
|
||||
RA: 'static + Send + Sync,
|
||||
SC: SelectChain<Block> + 'static,
|
||||
NumberFor<Block>: BlockNumberOps,
|
||||
{
|
||||
type Timer = Box<dyn Future<Item = (), Error = Self::Error> + Send>;
|
||||
|
||||
@@ -28,6 +28,7 @@ use client::runtime_api::ApiExt;
|
||||
use consensus_common::{
|
||||
BlockImport, Error as ConsensusError, ErrorKind as ConsensusErrorKind,
|
||||
ImportBlock, ImportResult, JustificationImport, well_known_cache_keys,
|
||||
SelectChain,
|
||||
};
|
||||
use fg_primitives::GrandpaApi;
|
||||
use runtime_primitives::Justification;
|
||||
@@ -53,16 +54,17 @@ use crate::justification::GrandpaJustification;
|
||||
///
|
||||
/// When using GRANDPA, the block import worker should be using this block import
|
||||
/// object.
|
||||
pub struct GrandpaBlockImport<B, E, Block: BlockT<Hash=H256>, RA, PRA> {
|
||||
pub struct GrandpaBlockImport<B, E, Block: BlockT<Hash=H256>, RA, PRA, SC> {
|
||||
inner: Arc<Client<B, E, Block, RA>>,
|
||||
select_chain: SC,
|
||||
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||
send_voter_commands: mpsc::UnboundedSender<VoterCommand<Block::Hash, NumberFor<Block>>>,
|
||||
consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
|
||||
api: Arc<PRA>,
|
||||
}
|
||||
|
||||
impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> JustificationImport<Block>
|
||||
for GrandpaBlockImport<B, E, Block, RA, PRA> where
|
||||
impl<B, E, Block: BlockT<Hash=H256>, RA, PRA, SC> JustificationImport<Block>
|
||||
for GrandpaBlockImport<B, E, Block, RA, PRA, SC> where
|
||||
NumberFor<Block>: grandpa::BlockNumberOps,
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
|
||||
@@ -70,6 +72,7 @@ impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> JustificationImport<Block>
|
||||
RA: Send + Sync,
|
||||
PRA: ProvideRuntimeApi,
|
||||
PRA::Api: GrandpaApi<Block>,
|
||||
SC: SelectChain<Block>,
|
||||
{
|
||||
type Error = ConsensusError;
|
||||
|
||||
@@ -86,7 +89,7 @@ impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> JustificationImport<Block>
|
||||
pending_change.effective_number() > chain_info.finalized_number &&
|
||||
pending_change.effective_number() <= chain_info.best_number
|
||||
{
|
||||
let effective_block_hash = self.inner.best_containing(
|
||||
let effective_block_hash = self.select_chain.finality_target(
|
||||
pending_change.canon_hash,
|
||||
Some(pending_change.effective_number()),
|
||||
);
|
||||
@@ -155,7 +158,9 @@ impl<'a, Block: 'a + BlockT> Drop for PendingSetChanges<'a, Block> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> GrandpaBlockImport<B, E, Block, RA, PRA> where
|
||||
impl<B, E, Block: BlockT<Hash=H256>, RA, PRA, SC>
|
||||
GrandpaBlockImport<B, E, Block, RA, PRA, SC>
|
||||
where
|
||||
NumberFor<Block>: grandpa::BlockNumberOps,
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
|
||||
@@ -371,8 +376,8 @@ impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> GrandpaBlockImport<B, E, Block, RA
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> BlockImport<Block>
|
||||
for GrandpaBlockImport<B, E, Block, RA, PRA> where
|
||||
impl<B, E, Block: BlockT<Hash=H256>, RA, PRA, SC> BlockImport<Block>
|
||||
for GrandpaBlockImport<B, E, Block, RA, PRA, SC> where
|
||||
NumberFor<Block>: grandpa::BlockNumberOps,
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
|
||||
@@ -504,16 +509,20 @@ impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> BlockImport<Block>
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> GrandpaBlockImport<B, E, Block, RA, PRA> {
|
||||
impl<B, E, Block: BlockT<Hash=H256>, RA, PRA, SC>
|
||||
GrandpaBlockImport<B, E, Block, RA, PRA, SC>
|
||||
{
|
||||
pub(crate) fn new(
|
||||
inner: Arc<Client<B, E, Block, RA>>,
|
||||
select_chain: SC,
|
||||
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||
send_voter_commands: mpsc::UnboundedSender<VoterCommand<Block::Hash, NumberFor<Block>>>,
|
||||
consensus_changes: SharedConsensusChanges<Block::Hash, NumberFor<Block>>,
|
||||
api: Arc<PRA>,
|
||||
) -> GrandpaBlockImport<B, E, Block, RA, PRA> {
|
||||
) -> GrandpaBlockImport<B, E, Block, RA, PRA, SC> {
|
||||
GrandpaBlockImport {
|
||||
inner,
|
||||
select_chain,
|
||||
authority_set,
|
||||
send_voter_commands,
|
||||
consensus_changes,
|
||||
@@ -522,12 +531,13 @@ impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> GrandpaBlockImport<B, E, Block, RA
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, E, Block: BlockT<Hash=H256>, RA, PRA> GrandpaBlockImport<B, E, Block, RA, PRA>
|
||||
where
|
||||
NumberFor<Block>: grandpa::BlockNumberOps,
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
|
||||
RA: Send + Sync,
|
||||
impl<B, E, Block: BlockT<Hash=H256>, RA, PRA, SC>
|
||||
GrandpaBlockImport<B, E, Block, RA, PRA, SC>
|
||||
where
|
||||
NumberFor<Block>: grandpa::BlockNumberOps,
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
|
||||
RA: Send + Sync,
|
||||
{
|
||||
|
||||
/// Import a block justification and finalize the block.
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
//! or prune any signaled changes based on whether the signaling block is
|
||||
//! included in the newly-finalized chain.
|
||||
#![forbid(warnings)]
|
||||
#![allow(deprecated)] // FIXME #2532: remove once the refactor is done https://github.com/paritytech/substrate/issues/2532
|
||||
|
||||
use futures::prelude::*;
|
||||
use log::{debug, info, warn};
|
||||
use futures::sync::mpsc;
|
||||
@@ -67,6 +69,7 @@ use runtime_primitives::traits::{
|
||||
use fg_primitives::GrandpaApi;
|
||||
use inherents::InherentDataProviders;
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use consensus_common::SelectChain;
|
||||
use substrate_primitives::{ed25519, H256, Pair, Blake2Hasher};
|
||||
use substrate_telemetry::{telemetry, CONSENSUS_INFO, CONSENSUS_DEBUG, CONSENSUS_WARN};
|
||||
use serde_json;
|
||||
@@ -285,24 +288,30 @@ impl<H, N> fmt::Display for CommandOrError<H, N> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LinkHalf<B, E, Block: BlockT<Hash=H256>, RA> {
|
||||
pub struct LinkHalf<B, E, Block: BlockT<Hash=H256>, RA, SC> {
|
||||
client: Arc<Client<B, E, Block, RA>>,
|
||||
select_chain: SC,
|
||||
persistent_data: PersistentData<Block>,
|
||||
voter_commands_rx: mpsc::UnboundedReceiver<VoterCommand<Block::Hash, NumberFor<Block>>>,
|
||||
}
|
||||
|
||||
/// Make block importer and link half necessary to tie the background voter
|
||||
/// to it.
|
||||
pub fn block_import<B, E, Block: BlockT<Hash=H256>, RA, PRA>(
|
||||
pub fn block_import<B, E, Block: BlockT<Hash=H256>, RA, PRA, SC>(
|
||||
client: Arc<Client<B, E, Block, RA>>,
|
||||
api: Arc<PRA>
|
||||
) -> Result<(GrandpaBlockImport<B, E, Block, RA, PRA>, LinkHalf<B, E, Block, RA>), ClientError>
|
||||
where
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
|
||||
RA: Send + Sync,
|
||||
PRA: ProvideRuntimeApi,
|
||||
PRA::Api: GrandpaApi<Block>,
|
||||
api: Arc<PRA>,
|
||||
select_chain: SC
|
||||
) -> Result<(
|
||||
GrandpaBlockImport<B, E, Block, RA, PRA, SC>,
|
||||
LinkHalf<B, E, Block, RA, SC>
|
||||
), ClientError>
|
||||
where
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
|
||||
RA: Send + Sync,
|
||||
PRA: ProvideRuntimeApi,
|
||||
PRA::Api: GrandpaApi<Block>,
|
||||
SC: SelectChain<Block>,
|
||||
{
|
||||
use runtime_primitives::traits::Zero;
|
||||
|
||||
@@ -328,6 +337,7 @@ pub fn block_import<B, E, Block: BlockT<Hash=H256>, RA, PRA>(
|
||||
Ok((
|
||||
GrandpaBlockImport::new(
|
||||
client.clone(),
|
||||
select_chain.clone(),
|
||||
persistent_data.authority_set.clone(),
|
||||
voter_commands_tx,
|
||||
persistent_data.consensus_changes.clone(),
|
||||
@@ -335,6 +345,7 @@ pub fn block_import<B, E, Block: BlockT<Hash=H256>, RA, PRA>(
|
||||
),
|
||||
LinkHalf {
|
||||
client,
|
||||
select_chain,
|
||||
persistent_data,
|
||||
voter_commands_rx,
|
||||
},
|
||||
@@ -435,11 +446,11 @@ fn register_finality_tracker_inherent_data_provider<B, E, Block: BlockT<Hash=H25
|
||||
}
|
||||
|
||||
/// Parameters used to run Grandpa.
|
||||
pub struct GrandpaParams<'a, B, E, Block: BlockT<Hash=H256>, N, RA, X> {
|
||||
pub struct GrandpaParams<'a, B, E, Block: BlockT<Hash=H256>, N, RA, SC, X> {
|
||||
/// Configuration for the GRANDPA service.
|
||||
pub config: Config,
|
||||
/// A link to the block import worker.
|
||||
pub link: LinkHalf<B, E, Block, RA>,
|
||||
pub link: LinkHalf<B, E, Block, RA, SC>,
|
||||
/// The Network instance.
|
||||
pub network: N,
|
||||
/// The inherent data providers.
|
||||
@@ -452,14 +463,15 @@ pub struct GrandpaParams<'a, B, E, Block: BlockT<Hash=H256>, N, RA, X> {
|
||||
|
||||
/// Run a GRANDPA voter as a task. Provide configuration and a link to a
|
||||
/// block import worker that has already been instantiated with `block_import`.
|
||||
pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, X>(
|
||||
grandpa_params: GrandpaParams<B, E, Block, N, RA, X>,
|
||||
pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, SC, X>(
|
||||
grandpa_params: GrandpaParams<B, E, Block, N, RA, SC, X>,
|
||||
) -> ::client::error::Result<impl Future<Item=(),Error=()> + Send + 'static> where
|
||||
Block::Hash: Ord,
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
|
||||
N: Network<Block> + Send + Sync + 'static,
|
||||
N::In: Send + 'static,
|
||||
SC: SelectChain<Block> + 'static,
|
||||
NumberFor<Block>: BlockNumberOps,
|
||||
DigestFor<Block>: Encode,
|
||||
RA: Send + Sync + 'static,
|
||||
@@ -480,6 +492,7 @@ pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, X>(
|
||||
|
||||
let LinkHalf {
|
||||
client,
|
||||
select_chain,
|
||||
persistent_data,
|
||||
voter_commands_rx,
|
||||
} = link;
|
||||
@@ -513,6 +526,7 @@ pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, X>(
|
||||
let initial_environment = Arc::new(Environment {
|
||||
inner: client.clone(),
|
||||
config: config.clone(),
|
||||
select_chain: select_chain.clone(),
|
||||
voters: Arc::new(voters),
|
||||
network: network.clone(),
|
||||
set_id: authority_set.set_id(),
|
||||
@@ -599,6 +613,7 @@ pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, X>(
|
||||
let client = client.clone();
|
||||
let config = config.clone();
|
||||
let network = network.clone();
|
||||
let select_chain = select_chain.clone();
|
||||
let authority_set = authority_set.clone();
|
||||
let consensus_changes = consensus_changes.clone();
|
||||
|
||||
@@ -636,6 +651,7 @@ pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, X>(
|
||||
|
||||
let env = Arc::new(Environment {
|
||||
inner: client,
|
||||
select_chain,
|
||||
config,
|
||||
voters: Arc::new(new.authorities.into_iter().collect()),
|
||||
set_id: new.set_id,
|
||||
@@ -704,14 +720,15 @@ pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, X>(
|
||||
}
|
||||
|
||||
#[deprecated(since = "1.1", note = "Please switch to run_grandpa_voter.")]
|
||||
pub fn run_grandpa<B, E, Block: BlockT<Hash=H256>, N, RA, X>(
|
||||
grandpa_params: GrandpaParams<B, E, Block, N, RA, X>,
|
||||
pub fn run_grandpa<B, E, Block: BlockT<Hash=H256>, N, RA, SC, X>(
|
||||
grandpa_params: GrandpaParams<B, E, Block, N, RA, SC, X>,
|
||||
) -> ::client::error::Result<impl Future<Item=(),Error=()> + Send + 'static> where
|
||||
Block::Hash: Ord,
|
||||
B: Backend<Block, Blake2Hasher> + 'static,
|
||||
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
|
||||
N: Network<Block> + Send + Sync + 'static,
|
||||
N::In: Send + 'static,
|
||||
SC: SelectChain<Block> + 'static,
|
||||
NumberFor<Block>: BlockNumberOps,
|
||||
DigestFor<Block>: Encode,
|
||||
RA: Send + Sync + 'static,
|
||||
|
||||
@@ -24,6 +24,7 @@ use grandpa::{
|
||||
};
|
||||
use log::{debug, info, warn};
|
||||
|
||||
use consensus_common::SelectChain;
|
||||
use client::{CallExecutor, Client, backend::Backend};
|
||||
use runtime_primitives::traits::{NumberFor, Block as BlockT};
|
||||
use substrate_primitives::{ed25519::Public as AuthorityId, H256, Blake2Hasher};
|
||||
@@ -143,9 +144,9 @@ fn grandpa_observer<B, E, Block: BlockT<Hash=H256>, RA, S>(
|
||||
/// listening for and validating GRANDPA commits instead of following the full
|
||||
/// protocol. Provide configuration and a link to a block import worker that has
|
||||
/// already been instantiated with `block_import`.
|
||||
pub fn run_grandpa_observer<B, E, Block: BlockT<Hash=H256>, N, RA>(
|
||||
pub fn run_grandpa_observer<B, E, Block: BlockT<Hash=H256>, N, RA, SC>(
|
||||
config: Config,
|
||||
link: LinkHalf<B, E, Block, RA>,
|
||||
link: LinkHalf<B, E, Block, RA, SC>,
|
||||
network: N,
|
||||
on_exit: impl Future<Item=(),Error=()> + Clone + Send + 'static,
|
||||
) -> ::client::error::Result<impl Future<Item=(),Error=()> + Send + 'static> where
|
||||
@@ -153,11 +154,13 @@ pub fn run_grandpa_observer<B, E, Block: BlockT<Hash=H256>, N, RA>(
|
||||
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
|
||||
N: Network<Block> + Send + Sync + 'static,
|
||||
N::In: Send + 'static,
|
||||
SC: SelectChain<Block> + 'static,
|
||||
NumberFor<Block>: BlockNumberOps,
|
||||
RA: Send + Sync + 'static,
|
||||
{
|
||||
let LinkHalf {
|
||||
client,
|
||||
select_chain: _,
|
||||
persistent_data,
|
||||
voter_commands_rx,
|
||||
} = link;
|
||||
|
||||
@@ -30,11 +30,13 @@ pub type BlockImportForService<F> = crate::GrandpaBlockImport<
|
||||
<F as ServiceFactory>::Block,
|
||||
<F as ServiceFactory>::RuntimeApi
|
||||
>,
|
||||
<F as ServiceFactory>::SelectChain
|
||||
>;
|
||||
|
||||
pub type LinkHalfForService<F> = crate::LinkHalf<
|
||||
FullBackend<F>,
|
||||
FullExecutor<F>,
|
||||
<F as ServiceFactory>::Block,
|
||||
<F as ServiceFactory>::RuntimeApi
|
||||
<F as ServiceFactory>::RuntimeApi,
|
||||
<F as ServiceFactory>::SelectChain
|
||||
>;
|
||||
|
||||
@@ -28,6 +28,7 @@ use client::{
|
||||
BlockchainEvents, error::Result,
|
||||
blockchain::Backend as BlockchainBackend,
|
||||
runtime_api::{Core, RuntimeVersion, ApiExt},
|
||||
LongestChain,
|
||||
};
|
||||
use test_client::{self, runtime::BlockNumber};
|
||||
use consensus_common::{BlockOrigin, ForkChoiceStrategy, ImportedAux, ImportBlock, ImportResult};
|
||||
@@ -50,6 +51,7 @@ type PeerData =
|
||||
test_client::Executor,
|
||||
Block,
|
||||
test_client::runtime::RuntimeApi,
|
||||
LongestChain<test_client::Backend, Block>
|
||||
>
|
||||
>
|
||||
>;
|
||||
@@ -106,9 +108,15 @@ impl TestNetFactory for GrandpaTestNet {
|
||||
fn make_block_import(&self, client: Arc<PeersClient>)
|
||||
-> (SharedBlockImport<Block>, Option<SharedJustificationImport<Block>>, PeerData)
|
||||
{
|
||||
|
||||
let select_chain = LongestChain::new(
|
||||
client.backend().clone(),
|
||||
client.import_lock().clone()
|
||||
);
|
||||
let (import, link) = block_import(
|
||||
client,
|
||||
Arc::new(self.test_config.clone())
|
||||
Arc::new(self.test_config.clone()),
|
||||
select_chain,
|
||||
).expect("Could not create block import for fresh peer.");
|
||||
let shared_import = Arc::new(import);
|
||||
(shared_import.clone(), Some(shared_import), Mutex::new(Some(link)))
|
||||
@@ -679,6 +687,7 @@ fn transition_3_voters_twice_1_full_observer() {
|
||||
link,
|
||||
)
|
||||
};
|
||||
|
||||
finality_notifications.push(
|
||||
client.finality_notification_stream()
|
||||
.take_while(|n| Ok(n.header.number() < &30))
|
||||
|
||||
@@ -127,7 +127,8 @@ pub fn import_blocks<F, E, R>(
|
||||
{
|
||||
let client = new_client::<F>(&config)?;
|
||||
// FIXME #1134 this shouldn't need a mutable config.
|
||||
let queue = components::FullComponents::<F>::build_import_queue(&mut config, client.clone())?;
|
||||
let select_chain = components::FullComponents::<F>::build_select_chain(&mut config, client.clone())?;
|
||||
let queue = components::FullComponents::<F>::build_import_queue(&mut config, client.clone(), select_chain)?;
|
||||
|
||||
let (wait_send, wait_recv) = std::sync::mpsc::channel();
|
||||
let wait_link = WaitLink::new(wait_send);
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
|
||||
//! Substrate service components.
|
||||
|
||||
use std::{sync::Arc, net::SocketAddr, marker::PhantomData, ops::Deref, ops::DerefMut};
|
||||
use std::{sync::Arc, net::SocketAddr, ops::Deref, ops::DerefMut};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use tokio::runtime::TaskExecutor;
|
||||
use crate::chain_spec::ChainSpec;
|
||||
use client_db;
|
||||
use client::{self, Client, runtime_api};
|
||||
use crate::{error, Service, maybe_start_server};
|
||||
use consensus_common::import_queue::ImportQueue;
|
||||
use consensus_common::{import_queue::ImportQueue, SelectChain};
|
||||
use network::{self, OnDemand};
|
||||
use substrate_executor::{NativeExecutor, NativeExecutionDispatch};
|
||||
use transaction_pool::txpool::{self, Options as TransactionPoolOptions, Pool as TransactionPool};
|
||||
@@ -304,9 +304,11 @@ pub trait ServiceFactory: 'static + Sized {
|
||||
/// Extended light service type.
|
||||
type LightService: ServiceTrait<LightComponents<Self>>;
|
||||
/// ImportQueue for full client
|
||||
type FullImportQueue: consensus_common::import_queue::ImportQueue<Self::Block> + 'static;
|
||||
type FullImportQueue: ImportQueue<Self::Block> + 'static;
|
||||
/// ImportQueue for light clients
|
||||
type LightImportQueue: consensus_common::import_queue::ImportQueue<Self::Block> + 'static;
|
||||
type LightImportQueue: ImportQueue<Self::Block> + 'static;
|
||||
/// The Fork Choice Strategy for the chain
|
||||
type SelectChain: SelectChain<Self::Block> + 'static;
|
||||
|
||||
//TODO: replace these with a constructor trait. that TransactionPool implements. (#1242)
|
||||
/// Extrinsic pool constructor for the full client.
|
||||
@@ -320,6 +322,12 @@ pub trait ServiceFactory: 'static + Sized {
|
||||
fn build_network_protocol(config: &FactoryFullConfiguration<Self>)
|
||||
-> Result<Self::NetworkProtocol, error::Error>;
|
||||
|
||||
/// Build the Fork Choice algorithm for full client
|
||||
fn build_select_chain(
|
||||
config: &mut FactoryFullConfiguration<Self>,
|
||||
client: Arc<FullClient<Self>>,
|
||||
) -> Result<Self::SelectChain, error::Error>;
|
||||
|
||||
/// Build full service.
|
||||
fn new_full(config: FactoryFullConfiguration<Self>, executor: TaskExecutor)
|
||||
-> Result<Self::FullService, error::Error>;
|
||||
@@ -330,7 +338,8 @@ pub trait ServiceFactory: 'static + Sized {
|
||||
/// ImportQueue for a full client
|
||||
fn build_full_import_queue(
|
||||
config: &mut FactoryFullConfiguration<Self>,
|
||||
_client: Arc<FullClient<Self>>
|
||||
_client: Arc<FullClient<Self>>,
|
||||
_select_chain: Self::SelectChain
|
||||
) -> Result<Self::FullImportQueue, error::Error> {
|
||||
if let Some(name) = config.chain_spec.consensus_engine() {
|
||||
match name {
|
||||
@@ -378,6 +387,8 @@ pub trait Components: Sized + 'static {
|
||||
>;
|
||||
/// Our Import Queue
|
||||
type ImportQueue: ImportQueue<FactoryBlock<Self::Factory>> + 'static;
|
||||
/// The Fork Choice Strategy for the chain
|
||||
type SelectChain: SelectChain<FactoryBlock<Self::Factory>>;
|
||||
|
||||
/// Create client.
|
||||
fn build_client(
|
||||
@@ -398,13 +409,20 @@ pub trait Components: Sized + 'static {
|
||||
/// instance of import queue for clients
|
||||
fn build_import_queue(
|
||||
config: &mut FactoryFullConfiguration<Self::Factory>,
|
||||
client: Arc<ComponentClient<Self>>
|
||||
client: Arc<ComponentClient<Self>>,
|
||||
select_chain: Self::SelectChain,
|
||||
) -> Result<Self::ImportQueue, error::Error>;
|
||||
|
||||
/// Build fork choice selector
|
||||
fn build_select_chain(
|
||||
config: &mut FactoryFullConfiguration<Self::Factory>,
|
||||
client: Arc<ComponentClient<Self>>
|
||||
) -> Result<Self::SelectChain, error::Error>;
|
||||
|
||||
}
|
||||
|
||||
/// A struct that implement `Components` for the full client.
|
||||
pub struct FullComponents<Factory: ServiceFactory> {
|
||||
_factory: PhantomData<Factory>,
|
||||
service: Service<FullComponents<Factory>>,
|
||||
}
|
||||
|
||||
@@ -416,7 +434,6 @@ impl<Factory: ServiceFactory> FullComponents<Factory> {
|
||||
) -> Result<Self, error::Error> {
|
||||
Ok(
|
||||
Self {
|
||||
_factory: Default::default(),
|
||||
service: Service::new(config, task_executor)?,
|
||||
}
|
||||
)
|
||||
@@ -445,6 +462,7 @@ impl<Factory: ServiceFactory> Components for FullComponents<Factory> {
|
||||
type ImportQueue = Factory::FullImportQueue;
|
||||
type RuntimeApi = Factory::RuntimeApi;
|
||||
type RuntimeServices = Factory::FullService;
|
||||
type SelectChain = Factory::SelectChain;
|
||||
|
||||
fn build_client(
|
||||
config: &FactoryFullConfiguration<Factory>,
|
||||
@@ -469,23 +487,32 @@ impl<Factory: ServiceFactory> Components for FullComponents<Factory> {
|
||||
)?), None))
|
||||
}
|
||||
|
||||
fn build_transaction_pool(config: TransactionPoolOptions, client: Arc<ComponentClient<Self>>)
|
||||
-> Result<TransactionPool<Self::TransactionPoolApi>, error::Error>
|
||||
{
|
||||
fn build_transaction_pool(
|
||||
config: TransactionPoolOptions,
|
||||
client: Arc<ComponentClient<Self>>
|
||||
) -> Result<TransactionPool<Self::TransactionPoolApi>, error::Error> {
|
||||
Factory::build_full_transaction_pool(config, client)
|
||||
}
|
||||
|
||||
fn build_import_queue(
|
||||
config: &mut FactoryFullConfiguration<Self::Factory>,
|
||||
client: Arc<ComponentClient<Self>>
|
||||
client: Arc<ComponentClient<Self>>,
|
||||
select_chain: Self::SelectChain,
|
||||
) -> Result<Self::ImportQueue, error::Error> {
|
||||
Factory::build_full_import_queue(config, client)
|
||||
Factory::build_full_import_queue(config, client, select_chain)
|
||||
}
|
||||
|
||||
fn build_select_chain(
|
||||
config: &mut FactoryFullConfiguration<Self::Factory>,
|
||||
client: Arc<ComponentClient<Self>>
|
||||
) -> Result<Self::SelectChain, error::Error> {
|
||||
Self::Factory::build_select_chain(config, client)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// A struct that implement `Components` for the light client.
|
||||
pub struct LightComponents<Factory: ServiceFactory> {
|
||||
_factory: PhantomData<Factory>,
|
||||
service: Service<LightComponents<Factory>>,
|
||||
}
|
||||
|
||||
@@ -497,7 +524,6 @@ impl<Factory: ServiceFactory> LightComponents<Factory> {
|
||||
) -> Result<Self, error::Error> {
|
||||
Ok(
|
||||
Self {
|
||||
_factory: Default::default(),
|
||||
service: Service::new(config, task_executor)?,
|
||||
}
|
||||
)
|
||||
@@ -520,6 +546,7 @@ impl<Factory: ServiceFactory> Components for LightComponents<Factory> {
|
||||
type ImportQueue = <Factory as ServiceFactory>::LightImportQueue;
|
||||
type RuntimeApi = Factory::RuntimeApi;
|
||||
type RuntimeServices = Factory::LightService;
|
||||
type SelectChain = Factory::SelectChain;
|
||||
|
||||
fn build_client(
|
||||
config: &FactoryFullConfiguration<Factory>,
|
||||
@@ -554,16 +581,27 @@ impl<Factory: ServiceFactory> Components for LightComponents<Factory> {
|
||||
|
||||
fn build_import_queue(
|
||||
config: &mut FactoryFullConfiguration<Self::Factory>,
|
||||
client: Arc<ComponentClient<Self>>
|
||||
client: Arc<ComponentClient<Self>>,
|
||||
_select_chain: Self::SelectChain,
|
||||
) -> Result<Self::ImportQueue, error::Error> {
|
||||
Factory::build_light_import_queue(config, client)
|
||||
}
|
||||
|
||||
/// Build fork choice selector
|
||||
fn build_select_chain(
|
||||
_config: &mut FactoryFullConfiguration<Self::Factory>,
|
||||
_client: Arc<ComponentClient<Self>>
|
||||
) -> Result<Self::SelectChain, error::Error> {
|
||||
Err("Fork choice doesn't happen on light clients.".into())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use consensus_common::BlockOrigin;
|
||||
use client::LongestChain;
|
||||
use substrate_test_client::{self, TestClient, AccountKeyring, runtime::Transfer};
|
||||
|
||||
#[test]
|
||||
@@ -576,8 +614,11 @@ mod tests {
|
||||
from: AccountKeyring::Alice.into(),
|
||||
to: Default::default(),
|
||||
}.into_signed_tx();
|
||||
let best = LongestChain::new(client.backend().clone(), client.import_lock())
|
||||
.best_chain().unwrap();
|
||||
|
||||
// store the transaction in the pool
|
||||
pool.submit_one(&BlockId::hash(client.best_block_header().unwrap().hash()), transaction.clone()).unwrap();
|
||||
pool.submit_one(&BlockId::hash(best.hash()), transaction.clone()).unwrap();
|
||||
|
||||
// import the block
|
||||
let mut builder = client.new_block().unwrap();
|
||||
|
||||
@@ -42,6 +42,7 @@ use primitives::Pair;
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use runtime_primitives::traits::{Header, As};
|
||||
use substrate_executor::NativeExecutor;
|
||||
use consensus_common::SelectChain;
|
||||
use tel::{telemetry, SUBSTRATE_INFO};
|
||||
|
||||
pub use self::error::{ErrorKind, Error};
|
||||
@@ -73,6 +74,7 @@ const DEFAULT_PROTOCOL_ID: &str = "sup";
|
||||
/// Substrate service.
|
||||
pub struct Service<Components: components::Components> {
|
||||
client: Arc<ComponentClient<Components>>,
|
||||
select_chain: <Components as components::Components>::SelectChain,
|
||||
network: Option<Arc<components::NetworkService<Components::Factory>>>,
|
||||
transaction_pool: Arc<TransactionPool<Components::TransactionPoolApi>>,
|
||||
inherents_pool: Arc<InherentsPool<ComponentExtrinsic<Components>>>,
|
||||
@@ -150,8 +152,13 @@ impl<Components: components::Components> Service<Components> {
|
||||
};
|
||||
|
||||
let (client, on_demand) = Components::build_client(&config, executor)?;
|
||||
let import_queue = Box::new(Components::build_import_queue(&mut config, client.clone())?);
|
||||
let best_header = client.best_block_header()?;
|
||||
let select_chain = Components::build_select_chain(&mut config, client.clone())?;
|
||||
let import_queue = Box::new(Components::build_import_queue(
|
||||
&mut config,
|
||||
client.clone(),
|
||||
select_chain.clone()
|
||||
)?);
|
||||
let best_header = select_chain.best_chain()?;
|
||||
|
||||
let version = config.full_version();
|
||||
info!("Best block: #{}", best_header.number());
|
||||
@@ -357,6 +364,7 @@ impl<Components: components::Components> Service<Components> {
|
||||
Ok(Service {
|
||||
client,
|
||||
network: Some(network),
|
||||
select_chain,
|
||||
transaction_pool,
|
||||
inherents_pool,
|
||||
signal: Some(signal),
|
||||
@@ -395,6 +403,11 @@ impl<Components> Service<Components> where Components: components::Components {
|
||||
self.client.clone()
|
||||
}
|
||||
|
||||
/// Get clone of select chain.
|
||||
pub fn select_chain(&self) -> <Components as components::Components>::SelectChain {
|
||||
self.select_chain.clone()
|
||||
}
|
||||
|
||||
/// Get shared network instance.
|
||||
pub fn network(&self) -> Arc<components::NetworkService<Components::Factory>> {
|
||||
self.network.as_ref().expect("self.network always Some").clone()
|
||||
@@ -578,6 +591,8 @@ macro_rules! construct_service_factory {
|
||||
{ $( $full_import_queue_init:tt )* },
|
||||
LightImportQueue = $light_import_queue:ty
|
||||
{ $( $light_import_queue_init:tt )* },
|
||||
SelectChain = $select_chain:ty
|
||||
{ $( $select_chain_init:tt )* },
|
||||
}
|
||||
) => {
|
||||
$( #[$attr] )*
|
||||
@@ -597,6 +612,7 @@ macro_rules! construct_service_factory {
|
||||
type LightService = $light_service;
|
||||
type FullImportQueue = $full_import_queue;
|
||||
type LightImportQueue = $light_import_queue;
|
||||
type SelectChain = $select_chain;
|
||||
|
||||
fn build_full_transaction_pool(
|
||||
config: $crate::TransactionPoolOptions,
|
||||
@@ -620,11 +636,19 @@ macro_rules! construct_service_factory {
|
||||
( $( $protocol_init )* ) (config)
|
||||
}
|
||||
|
||||
fn build_select_chain(
|
||||
config: &mut $crate::FactoryFullConfiguration<Self>,
|
||||
client: Arc<$crate::FullClient<Self>>
|
||||
) -> $crate::Result<Self::SelectChain, $crate::Error> {
|
||||
( $( $select_chain_init )* ) (config, client)
|
||||
}
|
||||
|
||||
fn build_full_import_queue(
|
||||
config: &mut $crate::FactoryFullConfiguration<Self>,
|
||||
client: $crate::Arc<$crate::FullClient<Self>>,
|
||||
select_chain: Self::SelectChain
|
||||
) -> $crate::Result<Self::FullImportQueue, $crate::Error> {
|
||||
( $( $full_import_queue_init )* ) (config, client)
|
||||
( $( $full_import_queue_init )* ) (config, client, select_chain)
|
||||
}
|
||||
|
||||
fn build_light_import_queue(
|
||||
|
||||
@@ -27,6 +27,8 @@ use state_machine::{
|
||||
execution_proof_check_on_trie_backend,
|
||||
};
|
||||
|
||||
use client::LongestChain;
|
||||
use consensus_common::SelectChain;
|
||||
use codec::Encode;
|
||||
|
||||
fn calling_function_with_strat(strat: ExecutionStrategy) {
|
||||
@@ -156,7 +158,8 @@ fn record_proof_works() {
|
||||
let client = test_client::new_with_execution_strategy(ExecutionStrategy::Both);
|
||||
|
||||
let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
|
||||
let storage_root = client.best_block_header().unwrap().state_root().clone();
|
||||
let storage_root = LongestChain::new(client.backend().clone(), client.import_lock())
|
||||
.best_chain().unwrap().state_root().clone();
|
||||
|
||||
let transaction = Transfer {
|
||||
amount: 1000,
|
||||
|
||||
@@ -13,7 +13,7 @@ use substrate_service::{
|
||||
};
|
||||
use basic_authorship::ProposerFactory;
|
||||
use consensus::{import_queue, start_aura, AuraImportQueue, SlotDuration, NothingExtra};
|
||||
use substrate_client as client;
|
||||
use substrate_client::{self as client, LongestChain};
|
||||
use primitives::{ed25519::Pair, Pair as PairT};
|
||||
use inherents::InherentDataProviders;
|
||||
use network::construct_simple_protocol;
|
||||
@@ -69,6 +69,7 @@ construct_service_factory! {
|
||||
SlotDuration::get_or_compute(&*client)?,
|
||||
key.clone(),
|
||||
client.clone(),
|
||||
service.select_chain(),
|
||||
client,
|
||||
proposer,
|
||||
service.network(),
|
||||
@@ -86,7 +87,7 @@ construct_service_factory! {
|
||||
FullImportQueue = AuraImportQueue<
|
||||
Self::Block,
|
||||
>
|
||||
{ |config: &mut FactoryFullConfiguration<Self> , client: Arc<FullClient<Self>>| {
|
||||
{ |config: &mut FactoryFullConfiguration<Self> , client: Arc<FullClient<Self>>, _select_chain: Self::SelectChain| {
|
||||
import_queue::<_, _, _, Pair>(
|
||||
SlotDuration::get_or_compute(&*client)?,
|
||||
client.clone(),
|
||||
@@ -111,5 +112,13 @@ construct_service_factory! {
|
||||
).map_err(Into::into)
|
||||
}
|
||||
},
|
||||
SelectChain = LongestChain<FullBackend<Self>, Self::Block>
|
||||
{ |config: &FactoryFullConfiguration<Self>, client: Arc<FullClient<Self>>| {
|
||||
Ok(LongestChain::new(
|
||||
client.backend().clone(),
|
||||
client.import_lock()
|
||||
))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use client;
|
||||
use consensus::{import_queue, start_aura, AuraImportQueue, SlotDuration, NothingExtra};
|
||||
use client::{self, LongestChain};
|
||||
use consensus::{import_queue, start_aura, AuraImportQueue,
|
||||
SlotDuration, NothingExtra
|
||||
};
|
||||
use grandpa;
|
||||
use node_executor;
|
||||
use primitives::{Pair as PairT, ed25519};
|
||||
@@ -94,6 +96,7 @@ construct_service_factory! {
|
||||
SlotDuration::get_or_compute(&*client)?,
|
||||
key.clone(),
|
||||
client,
|
||||
service.select_chain(),
|
||||
block_import.clone(),
|
||||
proposer,
|
||||
service.network(),
|
||||
@@ -152,11 +155,11 @@ construct_service_factory! {
|
||||
LightService = LightComponents<Self>
|
||||
{ |config, executor| <LightComponents<Factory>>::new(config, executor) },
|
||||
FullImportQueue = AuraImportQueue<Self::Block>
|
||||
{ |config: &mut FactoryFullConfiguration<Self> , client: Arc<FullClient<Self>>| {
|
||||
{ |config: &mut FactoryFullConfiguration<Self> , client: Arc<FullClient<Self>>, select_chain: Self::SelectChain| {
|
||||
let slot_duration = SlotDuration::get_or_compute(&*client)?;
|
||||
let (block_import, link_half) =
|
||||
grandpa::block_import::<_, _, _, RuntimeApi, FullClient<Self>>(
|
||||
client.clone(), client.clone()
|
||||
grandpa::block_import::<_, _, _, RuntimeApi, FullClient<Self>, _>(
|
||||
client.clone(), client.clone(), select_chain
|
||||
)?;
|
||||
let block_import = Arc::new(block_import);
|
||||
let justification_import = block_import.clone();
|
||||
@@ -184,6 +187,14 @@ construct_service_factory! {
|
||||
).map_err(Into::into)
|
||||
}
|
||||
},
|
||||
SelectChain = LongestChain<FullBackend<Self>, Self::Block>
|
||||
{ |config: &FactoryFullConfiguration<Self>, client: Arc<FullClient<Self>>| {
|
||||
Ok(LongestChain::new(
|
||||
client.backend().clone(),
|
||||
client.import_lock()
|
||||
))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
spec_name: create_runtime_str!("node"),
|
||||
impl_name: create_runtime_str!("substrate-node"),
|
||||
authoring_version: 10,
|
||||
spec_version: 73,
|
||||
spec_version: 74,
|
||||
impl_version: 74,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user