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:
Benjamin Kampmann
2019-05-10 14:08:12 +02:00
committed by Gavin Wood
parent 32fdeed21c
commit 18ca0170c3
22 changed files with 717 additions and 339 deletions
+394 -235
View File
@@ -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]
+2 -1
View File
@@ -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};
+18 -11
View File
@@ -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,
+16 -8
View File
@@ -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))
}
}
+5 -5
View File
@@ -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,
+8 -9
View File
@@ -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>;
+25 -15
View File
@@ -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.
+33 -16
View File
@@ -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
>;
+10 -1
View File
@@ -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))
+2 -1
View File
@@ -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);
+58 -17
View File
@@ -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();
+27 -3
View File
@@ -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,
+11 -2
View File
@@ -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()
))
}
},
}
}
+16 -5
View File
@@ -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()
))
}
},
}
}
+1 -1
View File
@@ -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,
};