Merge branch 'rh-grandpa-dynamic2' of github.com:paritytech/substrate

This commit is contained in:
Robert Habermeier
2018-11-15 17:49:36 +01:00
29 changed files with 1833 additions and 478 deletions
+22 -3
View File
@@ -663,11 +663,12 @@ dependencies = [
[[package]] [[package]]
name = "finality-grandpa" name = "finality-grandpa"
version = "0.2.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1803,6 +1804,7 @@ dependencies = [
"srml-treasury 0.1.0", "srml-treasury 0.1.0",
"srml-upgrade-key 0.1.0", "srml-upgrade-key 0.1.0",
"substrate-client 0.1.0", "substrate-client 0.1.0",
"substrate-fg-primitives 0.1.0",
"substrate-keyring 0.1.0", "substrate-keyring 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
] ]
@@ -3178,20 +3180,37 @@ dependencies = [
"wasmi 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "substrate-fg-primitives"
version = "0.1.0"
dependencies = [
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-primitives 0.1.0",
"sr-std 0.1.0",
"substrate-client 0.1.0",
"substrate-primitives 0.1.0",
]
[[package]] [[package]]
name = "substrate-finality-grandpa" name = "substrate-finality-grandpa"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"finality-grandpa 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)",
"finality-grandpa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-primitives 0.1.0", "sr-primitives 0.1.0",
"substrate-client 0.1.0", "substrate-client 0.1.0",
"substrate-consensus-common 0.1.0",
"substrate-fg-primitives 0.1.0",
"substrate-keyring 0.1.0", "substrate-keyring 0.1.0",
"substrate-network 0.1.0", "substrate-network 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
"substrate-test-client 0.1.0",
"tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@@ -4362,7 +4381,7 @@ dependencies = [
"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596" "checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596"
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
"checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" "checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa"
"checksum finality-grandpa 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "20d8cf871510f0d57630e75f9e65f87cba29581ccab1f78666d8b2e422d0baa6" "checksum finality-grandpa 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "be6d2735e8f570474c7925a60ebe04ec0bdd9eea7cc4fddab78a0ecfdefec20e"
"checksum fixed-hash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a557e80084b05c32b455963ff565a9de6f2866da023d6671705c6aff6f65e01c" "checksum fixed-hash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a557e80084b05c32b455963ff565a9de6f2866da023d6671705c6aff6f65e01c"
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+1
View File
@@ -27,6 +27,7 @@ members = [
"core/consensus/rhd", "core/consensus/rhd",
"core/executor", "core/executor",
"core/finality-grandpa", "core/finality-grandpa",
"core/finality-grandpa/primitives",
"core/keyring", "core/keyring",
"core/network", "core/network",
"core/primitives", "core/primitives",
+21
View File
@@ -274,6 +274,18 @@ pub struct BlockImportOperation<Block: BlockT, H: Hasher> {
updates: MemoryDB<H>, updates: MemoryDB<H>,
changes_trie_updates: MemoryDB<H>, changes_trie_updates: MemoryDB<H>,
pending_block: Option<PendingBlock<Block>>, pending_block: Option<PendingBlock<Block>>,
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
}
impl<Block: BlockT, H: Hasher> BlockImportOperation<Block, H> {
fn apply_aux(&mut self, transaction: &mut DBTransaction) {
for (key, maybe_val) in self.aux_ops.drain(..) {
match maybe_val {
Some(val) => transaction.put_vec(columns::AUX, &key, val),
None => transaction.delete(columns::AUX, &key),
}
}
}
} }
impl<Block> client::backend::BlockImportOperation<Block, Blake2Hasher> impl<Block> client::backend::BlockImportOperation<Block, Blake2Hasher>
@@ -345,6 +357,13 @@ where Block: BlockT<Hash=H256>,
self.changes_trie_updates = update; self.changes_trie_updates = update;
Ok(()) Ok(())
} }
fn set_aux<I>(&mut self, ops: I) -> Result<(), client::error::Error>
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
{
self.aux_ops = ops.into_iter().collect();
Ok(())
}
} }
struct StorageDb<Block: BlockT> { struct StorageDb<Block: BlockT> {
@@ -636,6 +655,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
old_state: state, old_state: state,
updates: MemoryDB::default(), updates: MemoryDB::default(),
changes_trie_updates: MemoryDB::default(), changes_trie_updates: MemoryDB::default(),
aux_ops: Vec::new(),
}) })
} }
@@ -643,6 +663,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe
-> Result<(), client::error::Error> -> Result<(), client::error::Error>
{ {
let mut transaction = DBTransaction::new(); let mut transaction = DBTransaction::new();
operation.apply_aux(&mut transaction);
if let Some(pending_block) = operation.pending_block { if let Some(pending_block) = operation.pending_block {
let hash = pending_block.header.hash(); let hash = pending_block.header.hash();
+23 -13
View File
@@ -43,6 +43,7 @@ pub(crate) mod columns {
pub const HEADER: Option<u32> = Some(2); pub const HEADER: Option<u32> = Some(2);
pub const CACHE: Option<u32> = Some(3); pub const CACHE: Option<u32> = Some(3);
pub const CHT: Option<u32> = Some(4); pub const CHT: Option<u32> = Some(4);
pub const AUX: Option<u32> = Some(5);
} }
/// Prefix for headers CHT. /// Prefix for headers CHT.
@@ -285,6 +286,7 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block>
header: Block::Header, header: Block::Header,
authorities: Option<Vec<AuthorityId>>, authorities: Option<Vec<AuthorityId>>,
leaf_state: NewBlockState, leaf_state: NewBlockState,
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
) -> ClientResult<()> { ) -> ClientResult<()> {
let mut transaction = DBTransaction::new(); let mut transaction = DBTransaction::new();
@@ -300,6 +302,13 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block>
::utils::number_and_hash_to_lookup_key(number, hash) ::utils::number_and_hash_to_lookup_key(number, hash)
}; };
for (key, maybe_val) in aux_ops {
match maybe_val {
Some(val) => transaction.put_vec(columns::AUX, &key, val),
None => transaction.delete(columns::AUX, &key),
}
}
if leaf_state.is_best() { if leaf_state.is_best() {
// handle reorg. // handle reorg.
{ {
@@ -471,16 +480,17 @@ pub(crate) mod tests {
} }
} }
fn header_with_changes_trie(parent: &Hash, number: u64) -> Header { pub fn insert_block_with_extrinsics_root(
let mut header = default_header(parent, number); db: &LightStorage<Block>,
header.digest.logs.push(DigestItem::ChangesTrieRoot([(number % 256) as u8; 32].into())); parent: &Hash,
header number: u64,
} authorities: Option<Vec<AuthorityId>>,
extrinsics_root: Hash,
fn header_with_extrinsics_root(parent: &Hash, number: u64, extrinsics_root: Hash) -> Header { ) -> Hash {
let mut header = default_header(parent, number); let header = prepare_header(parent, number, extrinsics_root);
header.extrinsics_root = extrinsics_root; let hash = header.hash();
header db.import_header(header, authorities, NewBlockState::Best, Vec::new()).unwrap();
hash
} }
pub fn insert_block<F: Fn() -> Header>( pub fn insert_block<F: Fn() -> Header>(
@@ -490,7 +500,7 @@ pub(crate) mod tests {
) -> Hash { ) -> Hash {
let header = header(); let header = header();
let hash = header.hash(); let hash = header.hash();
db.import_header(header, authorities, NewBlockState::Best).unwrap(); db.import_header(header, authorities, NewBlockState::Best, Vec::new()).unwrap();
hash hash
} }
@@ -501,7 +511,7 @@ pub(crate) mod tests {
) -> Hash { ) -> Hash {
let header = header(); let header = header();
let hash = header.hash(); let hash = header.hash();
db.import_header(header, authorities, NewBlockState::Final).unwrap(); db.import_header(header, authorities, NewBlockState::Final, Vec::new()).unwrap();
hash hash
} }
@@ -512,7 +522,7 @@ pub(crate) mod tests {
) -> Hash { ) -> Hash {
let header = header(); let header = header();
let hash = header.hash(); let hash = header.hash();
db.import_header(header, authorities, NewBlockState::Normal).unwrap(); db.import_header(header, authorities, NewBlockState::Normal, Vec::new()).unwrap();
hash hash
} }
+3
View File
@@ -74,6 +74,9 @@ pub trait BlockImportOperation<Block, H> where
fn reset_storage(&mut self, top: StorageMap, children: ChildrenStorageMap) -> error::Result<H::Out>; fn reset_storage(&mut self, top: StorageMap, children: ChildrenStorageMap) -> error::Result<H::Out>;
/// Inject changes trie data into the database. /// Inject changes trie data into the database.
fn update_changes_trie(&mut self, update: MemoryDB<H>) -> error::Result<()>; fn update_changes_trie(&mut self, update: MemoryDB<H>) -> error::Result<()>;
/// Update auxiliary keys. Values are `None` if should be deleted.
fn set_aux<I>(&mut self, ops: I) -> error::Result<()>
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>;
} }
/// Client backend. Manages the data layer. /// Client backend. Manages the data layer.
+10 -3
View File
@@ -602,6 +602,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
body: Option<Vec<Block::Extrinsic>>, body: Option<Vec<Block::Extrinsic>>,
authorities: Option<Vec<AuthorityId>>, authorities: Option<Vec<AuthorityId>>,
finalized: bool, finalized: bool,
aux: Vec<(Vec<u8>, Option<Vec<u8>>)>,
) -> error::Result<ImportResult> where ) -> error::Result<ImportResult> where
RA: TaggedTransactionQueue<Block>, RA: TaggedTransactionQueue<Block>,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + Clone, E: CallExecutor<Block, Blake2Hasher> + Send + Sync + Clone,
@@ -695,6 +696,8 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
if let Some(Some(changes_update)) = changes_update { if let Some(Some(changes_update)) = changes_update {
transaction.update_changes_trie(changes_update)?; transaction.update_changes_trie(changes_update)?;
} }
transaction.set_aux(aux)?;
self.backend.commit_operation(transaction)?; self.backend.commit_operation(transaction)?;
if make_notifications { if make_notifications {
@@ -880,7 +883,9 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
/// TODO [snd] possibly implement this on blockchain::Backend and just redirect here /// TODO [snd] possibly implement this on blockchain::Backend and just redirect here
/// Returns `Ok(None)` if `target_hash` is not found in search space. /// Returns `Ok(None)` if `target_hash` is not found in search space.
/// TODO [snd] write down time complexity /// TODO [snd] write down time complexity
pub fn best_containing(&self, target_hash: Block::Hash, maybe_max_number: Option<NumberFor<Block>>) -> error::Result<Option<Block::Hash>> { pub fn best_containing(&self, target_hash: Block::Hash, maybe_max_number: Option<NumberFor<Block>>)
-> error::Result<Option<Block::Hash>>
{
let target_header = { let target_header = {
match self.backend.blockchain().header(BlockId::Hash(target_hash))? { match self.backend.blockchain().header(BlockId::Hash(target_hash))? {
Some(x) => x, Some(x) => x,
@@ -1016,7 +1021,8 @@ impl<B, E, Block, RA> CallApiAt<Block> for Client<B, E, Block, RA> where
B: backend::Backend<Block, Blake2Hasher>, B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Clone + Send + Sync, E: CallExecutor<Block, Blake2Hasher> + Clone + Send + Sync,
Block: BlockT<Hash=H256>, Block: BlockT<Hash=H256>,
RA: CoreAPI<Block> RA: CoreAPI<Block>, // not strictly necessary at the moment
// but we want to bound to make sure the API is actually available.
{ {
fn call_api_at( fn call_api_at(
&self, &self,
@@ -1071,7 +1077,7 @@ impl<B, E, Block, RA> consensus::BlockImport<Block> for Client<B, E, Block, RA>
post_digests, post_digests,
body, body,
finalized, finalized,
.. auxiliary,
} = import_block; } = import_block;
let parent_hash = header.parent_hash().clone(); let parent_hash = header.parent_hash().clone();
@@ -1103,6 +1109,7 @@ impl<B, E, Block, RA> consensus::BlockImport<Block> for Client<B, E, Block, RA>
body, body,
new_authorities, new_authorities,
finalized, finalized,
auxiliary,
); );
*self.importing_block.write() = None; *self.importing_block.write() = None;
+31 -6
View File
@@ -97,6 +97,7 @@ struct BlockchainStorage<Block: BlockT> {
header_cht_roots: HashMap<NumberFor<Block>, Block::Hash>, header_cht_roots: HashMap<NumberFor<Block>, Block::Hash>,
changes_trie_cht_roots: HashMap<NumberFor<Block>, Block::Hash>, changes_trie_cht_roots: HashMap<NumberFor<Block>, Block::Hash>,
leaves: LeafSet<Block::Hash, NumberFor<Block>>, leaves: LeafSet<Block::Hash, NumberFor<Block>>,
aux: HashMap<Vec<u8>, Vec<u8>>,
} }
/// In-memory blockchain. Supports concurrent reads. /// In-memory blockchain. Supports concurrent reads.
@@ -146,6 +147,7 @@ impl<Block: BlockT> Blockchain<Block> {
header_cht_roots: HashMap::new(), header_cht_roots: HashMap::new(),
changes_trie_cht_roots: HashMap::new(), changes_trie_cht_roots: HashMap::new(),
leaves: LeafSet::new(), leaves: LeafSet::new(),
aux: HashMap::new(),
})); }));
Blockchain { Blockchain {
storage: storage.clone(), storage: storage.clone(),
@@ -249,6 +251,16 @@ impl<Block: BlockT> Blockchain<Block> {
self.storage.write().finalized_hash = hash; self.storage.write().finalized_hash = hash;
Ok(()) Ok(())
} }
fn write_aux(&self, ops: Vec<(Vec<u8>, Option<Vec<u8>>)>) {
let mut storage = self.storage.write();
for (k, v) in ops {
match v {
Some(v) => storage.aux.insert(k, v),
None => storage.aux.remove(&k),
};
}
}
} }
impl<Block: BlockT> HeaderBackend<Block> for Blockchain<Block> { impl<Block: BlockT> HeaderBackend<Block> for Blockchain<Block> {
@@ -322,6 +334,7 @@ impl<Block: BlockT> light::blockchain::Storage<Block> for Blockchain<Block>
header: Block::Header, header: Block::Header,
authorities: Option<Vec<AuthorityId>>, authorities: Option<Vec<AuthorityId>>,
state: NewBlockState, state: NewBlockState,
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
) -> error::Result<()> { ) -> error::Result<()> {
let hash = header.hash(); let hash = header.hash();
let parent_hash = *header.parent_hash(); let parent_hash = *header.parent_hash();
@@ -330,6 +343,7 @@ impl<Block: BlockT> light::blockchain::Storage<Block> for Blockchain<Block>
self.cache.insert(parent_hash, authorities); self.cache.insert(parent_hash, authorities);
} }
self.write_aux(aux_ops);
Ok(()) Ok(())
} }
@@ -363,6 +377,7 @@ pub struct BlockImportOperation<Block: BlockT, H: Hasher> {
old_state: InMemory<H>, old_state: InMemory<H>,
new_state: Option<InMemory<H>>, new_state: Option<InMemory<H>>,
changes_trie_update: Option<MemoryDB<H>>, changes_trie_update: Option<MemoryDB<H>>,
aux: Option<Vec<(Vec<u8>, Option<Vec<u8>>)>>,
} }
impl<Block, H> backend::BlockImportOperation<Block, H> for BlockImportOperation<Block, H> impl<Block, H> backend::BlockImportOperation<Block, H> for BlockImportOperation<Block, H>
@@ -433,6 +448,13 @@ where
self.new_state = Some(InMemory::from(transaction)); self.new_state = Some(InMemory::from(transaction));
Ok(root) Ok(root)
} }
fn set_aux<I>(&mut self, ops: I) -> error::Result<()>
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
{
self.aux = Some(ops.into_iter().collect());
Ok(())
}
} }
/// In-memory backend. Keeps all states and blocks in memory. Useful for testing. /// In-memory backend. Keeps all states and blocks in memory. Useful for testing.
@@ -445,7 +467,6 @@ where
states: RwLock<HashMap<Block::Hash, InMemory<H>>>, states: RwLock<HashMap<Block::Hash, InMemory<H>>>,
changes_trie_storage: InMemoryChangesTrieStorage<H>, changes_trie_storage: InMemoryChangesTrieStorage<H>,
blockchain: Blockchain<Block>, blockchain: Blockchain<Block>,
aux: RwLock<HashMap<Vec<u8>, Vec<u8>>>,
} }
impl<Block, H> Backend<Block, H> impl<Block, H> Backend<Block, H>
@@ -460,7 +481,6 @@ where
states: RwLock::new(HashMap::new()), states: RwLock::new(HashMap::new()),
changes_trie_storage: InMemoryChangesTrieStorage::new(), changes_trie_storage: InMemoryChangesTrieStorage::new(),
blockchain: Blockchain::new(), blockchain: Blockchain::new(),
aux: RwLock::new(HashMap::new()),
} }
} }
} }
@@ -488,6 +508,7 @@ where
old_state: state, old_state: state,
new_state: None, new_state: None,
changes_trie_update: None, changes_trie_update: None,
aux: None,
}) })
} }
@@ -515,6 +536,10 @@ where
self.blockchain.cache.insert(parent_hash, operation.pending_authorities); self.blockchain.cache.insert(parent_hash, operation.pending_authorities);
} }
} }
if let Some(ops) = operation.aux {
self.blockchain.write_aux(ops);
}
Ok(()) Ok(())
} }
@@ -542,18 +567,18 @@ where
} }
fn insert_aux<'a, 'b: 'a, 'c: 'a, I: IntoIterator<Item=&'a (&'c [u8], &'c [u8])>, D: IntoIterator<Item=&'a &'b [u8]>>(&self, insert: I, delete: D) -> error::Result<()> { fn insert_aux<'a, 'b: 'a, 'c: 'a, I: IntoIterator<Item=&'a (&'c [u8], &'c [u8])>, D: IntoIterator<Item=&'a &'b [u8]>>(&self, insert: I, delete: D) -> error::Result<()> {
let mut aux = self.aux.write(); let mut storage = self.blockchain.storage.write();
for (k, v) in insert { for (k, v) in insert {
aux.insert(k.to_vec(), v.to_vec()); storage.aux.insert(k.to_vec(), v.to_vec());
} }
for k in delete { for k in delete {
aux.remove(*k); storage.aux.remove(*k);
} }
Ok(()) Ok(())
} }
fn get_aux(&self, key: &[u8]) -> error::Result<Option<Vec<u8>>> { fn get_aux(&self, key: &[u8]) -> error::Result<Option<Vec<u8>>> {
Ok(self.aux.read().get(key).cloned()) Ok(self.blockchain.storage.read().aux.get(key).cloned())
} }
} }
@@ -46,6 +46,7 @@ pub struct ImportOperation<Block: BlockT, S, F> {
header: Option<Block::Header>, header: Option<Block::Header>,
authorities: Option<Vec<AuthorityId>>, authorities: Option<Vec<AuthorityId>>,
leaf_state: NewBlockState, leaf_state: NewBlockState,
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
_phantom: ::std::marker::PhantomData<(S, F)>, _phantom: ::std::marker::PhantomData<(S, F)>,
} }
@@ -86,6 +87,7 @@ impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F> where
header: None, header: None,
authorities: None, authorities: None,
leaf_state: NewBlockState::Normal, leaf_state: NewBlockState::Normal,
aux_ops: Vec::new(),
_phantom: Default::default(), _phantom: Default::default(),
}) })
} }
@@ -96,6 +98,7 @@ impl<S, F, Block, H> ClientBackend<Block, H> for Backend<S, F> where
header, header,
operation.authorities, operation.authorities,
operation.leaf_state, operation.leaf_state,
operation.aux_ops,
) )
} }
@@ -193,6 +196,13 @@ where
let mut op = in_mem.begin_operation(BlockId::Hash(Default::default()))?; let mut op = in_mem.begin_operation(BlockId::Hash(Default::default()))?;
op.reset_storage(top, children) op.reset_storage(top, children)
} }
fn set_aux<I>(&mut self, ops: I) -> ClientResult<()>
where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)>
{
self.aux_ops = ops.into_iter().collect();
Ok(())
}
} }
impl<Block, S, F, H> StateBackend<H> for OnDemandState<Block, S, F> impl<Block, S, F, H> StateBackend<H> for OnDemandState<Block, S, F>
@@ -35,11 +35,15 @@ use light::fetcher::{Fetcher, RemoteHeaderRequest};
/// Light client blockchain storage. /// Light client blockchain storage.
pub trait Storage<Block: BlockT>: BlockchainHeaderBackend<Block> { pub trait Storage<Block: BlockT>: BlockchainHeaderBackend<Block> {
/// Store new header. Should refuse to revert any finalized blocks. /// Store new header. Should refuse to revert any finalized blocks.
///
/// Takes new authorities, the leaf state of the new block, and
/// any auxiliary storage updates to place in the same operation.
fn import_header( fn import_header(
&self, &self,
header: Block::Header, header: Block::Header,
authorities: Option<Vec<AuthorityId>>, authorities: Option<Vec<AuthorityId>>,
state: NewBlockState, state: NewBlockState,
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
) -> ClientResult<()>; ) -> ClientResult<()>;
/// Mark historic header as finalized. /// Mark historic header as finalized.
@@ -16,6 +16,9 @@
//! Macros for declaring and implementing the runtime APIs. //! Macros for declaring and implementing the runtime APIs.
// these are part of the public API, so need to be re-exported
pub use runtime_version::{ApiId, RuntimeVersion};
/// Declare the given API traits. /// Declare the given API traits.
/// ///
/// # Example: /// # Example:
+25 -2
View File
@@ -24,12 +24,11 @@ pub use state_machine::OverlayedChanges;
pub use runtime_primitives::{traits::Block as BlockT, generic::BlockId}; pub use runtime_primitives::{traits::Block as BlockT, generic::BlockId};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use runtime_primitives::traits::ApiRef; use runtime_primitives::traits::ApiRef;
use runtime_version::ApiId; pub use runtime_version::ApiId;
#[doc(hidden)] #[doc(hidden)]
pub use rstd::slice; pub use rstd::slice;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use rstd::result; use rstd::result;
#[doc(hidden)]
pub use codec::{Encode, Decode}; pub use codec::{Encode, Decode};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use error; use error;
@@ -73,6 +72,30 @@ pub trait CallApiAt<Block: BlockT> {
changes: &mut OverlayedChanges, changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<Block>>, initialised_block: &mut Option<BlockId<Block>>,
) -> error::Result<Vec<u8>>; ) -> error::Result<Vec<u8>>;
/// Call the given api function with strong arguments at the given block
/// and returns the decoded result.
fn call_api_at_strong<In: Encode, Out: Decode>(
&self,
at: &BlockId<Block>,
function: &'static str,
args: &In,
changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<Block>>,
) -> error::Result<Out> where Self: Sized {
let raw = self.call_api_at(
at,
function,
args.encode(),
changes,
initialised_block,
)?;
match Out::decode(&mut &raw[..]) {
Some(out) => Ok(out),
None => bail!(error::ErrorKind::CallResultDecode(function)),
}
}
} }
/// The ApiIds for the various standard runtime APIs. /// The ApiIds for the various standard runtime APIs.
+8 -7
View File
@@ -389,11 +389,11 @@ pub type AuraImportQueue<B, C> = BasicQueue<B, AuraVerifier<C>>;
/// Start an import queue for the Aura consensus algorithm. /// Start an import queue for the Aura consensus algorithm.
pub fn import_queue<B, C>(config: Config, client: Arc<C>) -> AuraImportQueue<B, C> where pub fn import_queue<B, C>(config: Config, client: Arc<C>) -> AuraImportQueue<B, C> where
B: Block, B: Block,
C: Authorities<B> + BlockImport<B> + Send + Sync, C: Authorities<B> + BlockImport<B,Error=client::error::Error> + Send + Sync,
DigestItemFor<B>: CompatibleDigestItem, DigestItemFor<B>: CompatibleDigestItem,
{ {
let verifier = Arc::new(AuraVerifier { config, client }); let verifier = Arc::new(AuraVerifier { config, client: client.clone() });
BasicQueue::new(verifier) BasicQueue::new(verifier, client)
} }
@@ -443,12 +443,13 @@ mod tests {
const TEST_ROUTING_INTERVAL: Duration = Duration::from_millis(50); const TEST_ROUTING_INTERVAL: Duration = Duration::from_millis(50);
pub struct AuraTestNet { pub struct AuraTestNet {
peers: Vec<Arc<Peer<AuraVerifier<PeersClient>>>>, peers: Vec<Arc<Peer<AuraVerifier<PeersClient>, ()>>>,
started: bool started: bool
} }
impl TestNetFactory for AuraTestNet { impl TestNetFactory for AuraTestNet {
type Verifier = AuraVerifier<PeersClient>; type Verifier = AuraVerifier<PeersClient>;
type PeerData = ();
/// Create new test network with peers and given config. /// Create new test network with peers and given config.
fn from_config(_config: &ProtocolConfig) -> Self { fn from_config(_config: &ProtocolConfig) -> Self {
@@ -465,15 +466,15 @@ mod tests {
Arc::new(AuraVerifier { client, config }) Arc::new(AuraVerifier { client, config })
} }
fn peer(&self, i: usize) -> &Peer<Self::Verifier> { fn peer(&self, i: usize) -> &Peer<Self::Verifier, ()> {
&self.peers[i] &self.peers[i]
} }
fn peers(&self) -> &Vec<Arc<Peer<Self::Verifier>>> { fn peers(&self) -> &Vec<Arc<Peer<Self::Verifier, ()>>> {
&self.peers &self.peers
} }
fn mut_peers<F: Fn(&mut Vec<Arc<Peer<Self::Verifier>>>)>(&mut self, closure: F ) { fn mut_peers<F: Fn(&mut Vec<Arc<Peer<Self::Verifier, ()>>>)>(&mut self, closure: F) {
closure(&mut self.peers); closure(&mut self.peers);
} }
+7 -2
View File
@@ -6,17 +6,22 @@ authors = ["Parity Technologies <admin@parity.io>"]
[dependencies] [dependencies]
futures = "0.1.17" futures = "0.1.17"
parity-codec = "2.1" parity-codec = "2.1"
parity-codec-derive = "2.0"
sr-primitives = { path = "../sr-primitives" } sr-primitives = { path = "../sr-primitives" }
substrate-consensus-common = { path = "../consensus/common" }
substrate-primitives = { path = "../primitives" } substrate-primitives = { path = "../primitives" }
substrate-client = { path = "../client" } substrate-client = { path = "../client" }
log = "0.4" log = "0.4"
parking_lot = "0.4"
tokio = "0.1.7" tokio = "0.1.7"
substrate-fg-primitives = { path = "primitives" }
[dependencies.finality-grandpa] [dependencies.finality-grandpa]
version = "0.2.0" version = "0.3.0"
features = ["derive-codec"] features = ["derive-codec"]
[dev-dependencies] [dev-dependencies]
substrate-network = { path = "../network", features = ["test-helpers"] } substrate-network = { path = "../network", features = ["test-helpers"] }
parking_lot = "0.4"
substrate-keyring = { path = "../keyring" } substrate-keyring = { path = "../keyring" }
substrate-test-client = { path = "../test-client"}
env_logger = "0.5"
@@ -0,0 +1,23 @@
[package]
name = "substrate-fg-primitives"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
substrate-client = { path = "../../client", default-features = false }
substrate-primitives = { path = "../../primitives", default-features = false }
parity-codec = { version = "2.1", default-features = false }
parity-codec-derive = { version = "2.1", default-features = false }
sr-primitives = { path = "../../sr-primitives", default-features = false }
sr-std = { path = "../../sr-std", default-features = false }
[features]
default = ["std"]
std = [
"substrate-primitives/std",
"substrate-client/std",
"parity-codec/std",
"parity-codec-derive/std",
"sr-primitives/std",
"sr-std/std",
]
@@ -0,0 +1,86 @@
// Copyright 2018 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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 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. If not, see <http://www.gnu.org/licenses/>.
//! Primitives for GRANDPA integration, suitable for WASM compilation.
#![cfg_attr(not(feature = "std"), no_std)]
extern crate substrate_primitives;
extern crate sr_primitives;
extern crate parity_codec;
#[macro_use]
extern crate parity_codec_derive;
#[macro_use]
extern crate substrate_client as client;
extern crate sr_std as rstd;
use substrate_primitives::AuthorityId;
use sr_primitives::traits::{Block as BlockT, DigestFor, NumberFor};
use rstd::vec::Vec;
/// A scheduled change of authority set.
#[cfg_attr(feature = "std", derive(Debug, PartialEq))]
#[derive(Clone, Encode, Decode)]
pub struct ScheduledChange<N> {
/// The new authorities after the change, along with their respective weights.
pub next_authorities: Vec<(AuthorityId, u64)>,
/// The number of blocks to delay.
pub delay: N,
}
/// WASM function call to check for pending changes.
pub const PENDING_CHANGE_CALL: &str = "grandpa_pending_change";
/// WASM function call to get current GRANDPA authorities.
pub const AUTHORITIES_CALL: &str = "grandpa_authorities";
/// The ApiIds for GRANDPA API.
pub mod id {
use client::runtime_api::ApiId;
/// ApiId for the GrandpaApi trait.
pub const GRANDPA_API: ApiId = *b"fgrandpa";
}
decl_runtime_apis! {
/// APIs for integrating the GRANDPA finality gadget into runtimes.
/// This should be implemented on the runtime side.
///
/// This is primarily used for negotiating authority-set changes for the
/// gadget. GRANDPA uses a signalling model of changing authority sets:
/// changes should be signalled with a delay of N blocks, and then automatically
/// applied in the runtime after those N blocks have passed.
///
/// The consensus protocol will coordinate the handoff externally.
pub trait GrandpaApi<Block: BlockT> {
/// Check a digest for pending changes.
/// Return `None` if there are no pending changes.
///
/// Precedence towards earlier or later digest items can be given
/// based on the rules of the chain.
///
/// No change should be scheduled if one is already and the delay has not
/// passed completely.
fn grandpa_pending_change(digest: DigestFor<Block>)
-> Option<ScheduledChange<NumberFor<Block>>>;
/// Get the current GRANDPA authorities and weights. This should not change except
/// for when changes are scheduled and the corresponding delay has passed.
fn grandpa_authorities() -> Vec<(AuthorityId, u64)>;
}
}
@@ -0,0 +1,344 @@
// Copyright 2018 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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 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. If not, see <http://www.gnu.org/licenses/>.
//! Utilities for dealing with authorities, authority sets, and handoffs.
use parking_lot::RwLock;
use substrate_primitives::AuthorityId;
use std::cmp::Ord;
use std::fmt::Debug;
use std::ops::Add;
use std::sync::Arc;
/// A shared authority set.
pub(crate) struct SharedAuthoritySet<H, N> {
inner: Arc<RwLock<AuthoritySet<H, N>>>,
}
impl<H, N> Clone for SharedAuthoritySet<H, N> {
fn clone(&self) -> Self {
SharedAuthoritySet { inner: self.inner.clone() }
}
}
impl<H, N> SharedAuthoritySet<H, N> {
/// The genesis authority set.
pub(crate) fn genesis(initial: Vec<(AuthorityId, u64)>) -> Self {
SharedAuthoritySet {
inner: Arc::new(RwLock::new(AuthoritySet::genesis(initial)))
}
}
/// Acquire a reference to the inner read-write lock.
pub(crate) fn inner(&self) -> &RwLock<AuthoritySet<H, N>> {
&*self.inner
}
}
impl<H: Eq, N> SharedAuthoritySet<H, N>
where N: Add<Output=N> + Ord + Clone + Debug
{
/// Get the earliest limit-block number, if any.
pub(crate) fn current_limit(&self) -> Option<N> {
self.inner.read().current_limit()
}
/// Get the current set ID. This is incremented every time the set changes.
pub(crate) fn set_id(&self) -> u64 {
self.inner.read().set_id
}
}
impl<H, N> From<AuthoritySet<H, N>> for SharedAuthoritySet<H, N> {
fn from(set: AuthoritySet<H, N>) -> Self {
SharedAuthoritySet { inner: Arc::new(RwLock::new(set)) }
}
}
/// Status of the set after changes were applied.
pub(crate) struct Status<H, N> {
/// Whether internal changes were made.
pub(crate) changed: bool,
/// `Some` when underlying authority set has changed, containing the
/// block where that set changed.
pub(crate) new_set_block: Option<(H, N)>,
}
/// A set of authorities.
#[derive(Debug, Clone, Encode, Decode)]
pub(crate) struct AuthoritySet<H, N> {
current_authorities: Vec<(AuthorityId, u64)>,
set_id: u64,
pending_changes: Vec<PendingChange<H, N>>,
}
impl<H, N> AuthoritySet<H, N> {
/// Get a genesis set with given authorities.
pub(crate) fn genesis(initial: Vec<(AuthorityId, u64)>) -> Self {
AuthoritySet {
current_authorities: initial,
set_id: 0,
pending_changes: Vec::new(),
}
}
/// Get the current set id and a reference to the current authority set.
pub(crate) fn current(&self) -> (u64, &[(AuthorityId, u64)]) {
(self.set_id, &self.current_authorities[..])
}
}
impl<H: Eq, N> AuthoritySet<H, N>
where N: Add<Output=N> + Ord + Clone + Debug,
{
/// Note an upcoming pending transition.
pub(crate) fn add_pending_change(&mut self, pending: PendingChange<H, N>) {
// ordered first by effective number and then by signal-block number.
let key = (pending.effective_number(), pending.canon_height.clone());
let idx = self.pending_changes
.binary_search_by_key(&key, |change| (
change.effective_number(),
change.canon_height.clone(),
))
.unwrap_or_else(|i| i);
self.pending_changes.insert(idx, pending);
}
/// Inspect pending changes.
#[cfg(test)]
pub(crate) fn pending_changes(&self) -> &[PendingChange<H, N>] {
&self.pending_changes
}
/// Get the earliest limit-block number, if any.
pub(crate) fn current_limit(&self) -> Option<N> {
self.pending_changes.get(0).map(|change| change.effective_number().clone())
}
/// Apply or prune any pending transitions. Provide a closure that can be used to check for the
/// finalized block with given number.
///
/// When the set has changed, the return value will be `Ok(Some((H, N)))` which is the cnaonical
/// block where the set last changed.
pub(crate) fn apply_changes<F, E>(&mut self, just_finalized: N, mut canonical: F)
-> Result<Status<H, N>, E>
where F: FnMut(N) -> Result<H, E>
{
let mut status = Status {
changed: false,
new_set_block: None,
};
loop {
let remove_up_to = match self.pending_changes.first() {
None => break,
Some(change) => {
let effective_number = change.effective_number();
if effective_number > just_finalized { break }
// check if the block that signalled the change is canonical in
// our chain.
if canonical(change.canon_height.clone())? == change.canon_hash {
// apply this change: make the set canonical
info!(target: "finality", "Applying authority set change scheduled at block #{:?}",
change.canon_height);
self.current_authorities = change.next_authorities.clone();
self.set_id += 1;
status.new_set_block = Some((
canonical(effective_number.clone())?,
effective_number.clone(),
));
// discard any signalled changes
// that happened before or equal to the effective number of the change.
self.pending_changes.iter()
.take_while(|c| c.canon_height <= effective_number)
.count()
} else {
1 // prune out this entry; it's no longer relevant.
}
}
};
let remove_up_to = ::std::cmp::min(remove_up_to, self.pending_changes.len());
self.pending_changes.drain(..remove_up_to);
status.changed = true; // always changed because we strip at least the first change.
}
Ok(status)
}
}
/// A pending change to the authority set.
///
/// This will be applied when the announcing block is at some depth within
/// the finalized chain.
#[derive(Debug, Clone, Encode, Decode, PartialEq)]
pub(crate) struct PendingChange<H, N> {
/// The new authorities and weights to apply.
pub(crate) next_authorities: Vec<(AuthorityId, u64)>,
/// How deep in the finalized chain the announcing block must be
/// before the change is applied.
pub(crate) finalization_depth: N,
/// The announcing block's height.
pub(crate) canon_height: N,
/// The announcing block's hash.
pub(crate) canon_hash: H,
}
impl<H, N: Add<Output=N> + Clone> PendingChange<H, N> {
/// Returns the effective number this change will be applied at.
fn effective_number(&self) -> N {
self.canon_height.clone() + self.finalization_depth.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn changes_sorted_in_correct_order() {
let mut authorities = AuthoritySet {
current_authorities: Vec::new(),
set_id: 0,
pending_changes: Vec::new(),
};
let change_a = PendingChange {
next_authorities: Vec::new(),
finalization_depth: 10,
canon_height: 5,
canon_hash: "hash_a",
};
let change_b = PendingChange {
next_authorities: Vec::new(),
finalization_depth: 0,
canon_height: 16,
canon_hash: "hash_b",
};
let change_c = PendingChange {
next_authorities: Vec::new(),
finalization_depth: 5,
canon_height: 10,
canon_hash: "hash_c",
};
authorities.add_pending_change(change_a.clone());
authorities.add_pending_change(change_b.clone());
authorities.add_pending_change(change_c.clone());
assert_eq!(authorities.pending_changes, vec![change_a, change_c, change_b]);
}
#[test]
fn apply_change() {
let mut authorities = AuthoritySet {
current_authorities: Vec::new(),
set_id: 0,
pending_changes: Vec::new(),
};
let set_a = vec![([1; 32].into(), 5)];
let set_b = vec![([2; 32].into(), 5)];
let change_a = PendingChange {
next_authorities: set_a.clone(),
finalization_depth: 10,
canon_height: 5,
canon_hash: "hash_a",
};
let change_b = PendingChange {
next_authorities: set_b.clone(),
finalization_depth: 10,
canon_height: 5,
canon_hash: "hash_b",
};
authorities.add_pending_change(change_a.clone());
authorities.add_pending_change(change_b.clone());
authorities.apply_changes(10, |_| Err(())).unwrap();
assert!(authorities.current_authorities.is_empty());
authorities.apply_changes(15, |n| match n {
5 => Ok("hash_a"),
15 => Ok("hash_15_canon"),
_ => Err(()),
}).unwrap();
assert_eq!(authorities.current_authorities, set_a);
assert_eq!(authorities.set_id, 1);
assert!(authorities.pending_changes.is_empty());
}
#[test]
fn apply_many_changes_at_once() {
let mut authorities = AuthoritySet {
current_authorities: Vec::new(),
set_id: 0,
pending_changes: Vec::new(),
};
let set_a = vec![([1; 32].into(), 5)];
let set_b = vec![([2; 32].into(), 5)];
let set_c = vec![([3; 32].into(), 5)];
let change_a = PendingChange {
next_authorities: set_a.clone(),
finalization_depth: 10,
canon_height: 5,
canon_hash: "hash_a",
};
// will be ignored because it was signalled when change_a still pending.
let change_b = PendingChange {
next_authorities: set_b.clone(),
finalization_depth: 10,
canon_height: 15,
canon_hash: "hash_b",
};
let change_c = PendingChange {
next_authorities: set_c.clone(),
finalization_depth: 10,
canon_height: 16,
canon_hash: "hash_c",
};
authorities.add_pending_change(change_a.clone());
authorities.add_pending_change(change_b.clone());
authorities.add_pending_change(change_c.clone());
authorities.apply_changes(26, |n| match n {
5 => Ok("hash_a"),
15 => Ok("hash_b"),
16 => Ok("hash_c"),
26 => Ok("hash_26"),
_ => Err(()),
}).unwrap();
assert_eq!(authorities.current_authorities, set_c);
assert_eq!(authorities.set_id, 2); // has been bumped only twice
assert!(authorities.pending_changes.is_empty());
}
}
+456 -257
View File
@@ -16,15 +16,50 @@
//! Integration of the GRANDPA finality gadget into substrate. //! Integration of the GRANDPA finality gadget into substrate.
//! //!
//! This is a long-running future that produces finality notifications. //! This crate provides a long-running future that produces finality notifications.
//!
//! # Usage
//!
//! First, create a block-import wrapper with the `block_import` function.
//! The GRANDPA worker needs to be linked together with this block import object,
//! so a `LinkHalf` is returned as well. All blocks imported (from network or consensus or otherwise)
//! must pass through this wrapper, otherwise consensus is likely to break in
//! unexpected ways.
//!
//! Next, use the `LinkHalf` and a local configuration to `run_grandpa`. This requires a
//! `Network` implementation. The returned future should be driven to completion and
//! will finalize blocks in the background.
//!
//! # Changing authority sets
//!
//! The rough idea behind changing authority sets in GRANDPA is that at some point,
//! we obtain agreement for some maximum block height that the current set can
//! finalize, and once a block with that height is finalized the next set will
//! pick up finalization from there.
//!
//! Technically speaking, this would be implemented as a voting rule which says,
//! "if there is a signal for a change in N blocks in block B, only vote on
//! chains with length NUM(B) + N if they contain B". This conditional-inclusion
//! logic is complex to compute because it requires looking arbitrarily far
//! back in the chain.
//!
//! Instead, we keep track of a list of all signals we've seen so far,
//! sorted ascending by the block number they would be applied at. We never vote
//! on chains with number higher than the earliest handoff block number
//! (this is num(signal) + N). When finalizing a block, we either apply or prune
//! any signaled changes based on whether the signaling block is included in the
//! newly-finalized chain.
extern crate finality_grandpa as grandpa; extern crate finality_grandpa as grandpa;
extern crate futures; extern crate futures;
extern crate substrate_client as client; extern crate substrate_client as client;
extern crate sr_primitives as runtime_primitives; extern crate sr_primitives as runtime_primitives;
extern crate substrate_consensus_common as consensus_common;
extern crate substrate_primitives; extern crate substrate_primitives;
extern crate tokio; extern crate tokio;
extern crate parking_lot;
extern crate parity_codec as codec; extern crate parity_codec as codec;
extern crate substrate_fg_primitives as fg_primitives;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
@@ -33,44 +68,84 @@ extern crate log;
extern crate substrate_network as network; extern crate substrate_network as network;
#[cfg(test)] #[cfg(test)]
extern crate parking_lot; extern crate substrate_keyring as keyring;
#[cfg(test)] #[cfg(test)]
extern crate substrate_keyring as keyring; extern crate substrate_test_client as test_client;
#[cfg(test)]
extern crate env_logger;
#[macro_use]
extern crate parity_codec_derive;
use futures::prelude::*; use futures::prelude::*;
use futures::stream::Fuse; use futures::stream::Fuse;
use futures::sync::mpsc; use futures::sync::mpsc;
use client::{Client, ImportNotifications, backend::Backend, CallExecutor, blockchain::HeaderBackend}; use client::{Client, error::Error as ClientError, ImportNotifications, backend::Backend, CallExecutor};
use client::blockchain::HeaderBackend;
use client::runtime_api::TaggedTransactionQueue;
use codec::{Encode, Decode}; use codec::{Encode, Decode};
use runtime_primitives::traits::{As, NumberFor, Block as BlockT, Header as HeaderT}; use consensus_common::{BlockImport, ImportBlock, ImportResult};
use runtime_primitives::traits::{
NumberFor, Block as BlockT, Header as HeaderT, DigestFor, ProvideRuntimeApi
};
use fg_primitives::GrandpaApi;
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use substrate_primitives::{ed25519, H256, AuthorityId, Blake2Hasher}; use substrate_primitives::{ed25519, H256, AuthorityId, Blake2Hasher};
use tokio::timer::Interval; use tokio::timer::Interval;
use grandpa::Error as GrandpaError; use grandpa::Error as GrandpaError;
use grandpa::{voter, round::State as RoundState, Prevote, Precommit, Equivocation}; use grandpa::{voter, round::State as RoundState, Equivocation, BlockNumberOps};
use std::collections::{VecDeque, HashMap}; use std::collections::{VecDeque, HashMap};
use std::sync::Arc; use std::sync::Arc;
use std::time::{Instant, Duration}; use std::time::{Instant, Duration};
use authorities::SharedAuthoritySet;
pub use fg_primitives::ScheduledChange;
mod authorities;
#[cfg(test)]
mod tests;
const LAST_COMPLETED_KEY: &[u8] = b"grandpa_completed_round"; const LAST_COMPLETED_KEY: &[u8] = b"grandpa_completed_round";
const AUTHORITY_SET_KEY: &[u8] = b"grandpa_voters";
/// round-number, round-state
type LastCompleted<H, N> = (u64, RoundState<H, N>);
/// A GRANDPA message for a substrate chain. /// A GRANDPA message for a substrate chain.
pub type Message<Block> = grandpa::Message<<Block as BlockT>::Hash>; pub type Message<Block> = grandpa::Message<<Block as BlockT>::Hash, NumberFor<Block>>;
/// A signed message. /// A signed message.
pub type SignedMessage<Block> = grandpa::SignedMessage<<Block as BlockT>::Hash, ed25519::Signature, AuthorityId>; pub type SignedMessage<Block> = grandpa::SignedMessage<
<Block as BlockT>::Hash,
NumberFor<Block>,
ed25519::Signature,
AuthorityId,
>;
/// A prevote message for this chain's block type.
pub type Prevote<Block> = grandpa::Prevote<<Block as BlockT>::Hash, NumberFor<Block>>;
/// A precommit message for this chain's block type.
pub type Precommit<Block> = grandpa::Precommit<<Block as BlockT>::Hash, NumberFor<Block>>;
/// Configuration for the GRANDPA service. /// Configuration for the GRANDPA service.
#[derive(Clone)]
pub struct Config { pub struct Config {
/// The expected duration for a message to be gossiped across the network. /// The expected duration for a message to be gossiped across the network.
pub gossip_duration: Duration, pub gossip_duration: Duration,
/// The voters.
// TODO: make dynamic
pub voters: Vec<AuthorityId>,
/// The local signing key. /// The local signing key.
pub local_key: Option<Arc<ed25519::Pair>>, pub local_key: Option<Arc<ed25519::Pair>>,
/// Some local identifier of the voter.
pub name: Option<String>,
}
impl Config {
fn name(&self) -> &str {
self.name.as_ref().map(|s| s.as_str()).unwrap_or("<unknown>")
}
} }
/// Errors that can occur while voting in GRANDPA. /// Errors that can occur while voting in GRANDPA.
@@ -83,7 +158,7 @@ pub enum Error {
/// A blockchain error. /// A blockchain error.
Blockchain(String), Blockchain(String),
/// Could not complete a round on disk. /// Could not complete a round on disk.
CouldNotCompleteRound(::client::error::Error), Client(ClientError),
/// A timer failed to fire. /// A timer failed to fire.
Timer(::tokio::timer::Error), Timer(::tokio::timer::Error),
} }
@@ -104,13 +179,13 @@ pub trait Network: Clone {
/// Get a stream of messages for a specific round. This stream should /// Get a stream of messages for a specific round. This stream should
/// never logically conclude. /// never logically conclude.
fn messages_for(&self, round: u64) -> Self::In; fn messages_for(&self, round: u64, set_id: u64) -> Self::In;
/// Send a message at a specific round out. /// Send a message at a specific round out.
fn send_message(&self, round: u64, message: Vec<u8>); fn send_message(&self, round: u64, set_id: u64, message: Vec<u8>);
/// Clean up messages for a round. /// Clean up messages for a round.
fn drop_messages(&self, round: u64); fn drop_messages(&self, round: u64, set_id: u64);
} }
/// Something which can determine if a block is known. /// Something which can determine if a block is known.
@@ -118,24 +193,23 @@ pub trait BlockStatus<Block: BlockT> {
/// Return `Ok(Some(number))` or `Ok(None)` depending on whether the block /// Return `Ok(Some(number))` or `Ok(None)` depending on whether the block
/// is definitely known and has been imported. /// is definitely known and has been imported.
/// If an unexpected error occurs, return that. /// If an unexpected error occurs, return that.
fn block_number(&self, hash: Block::Hash) -> Result<Option<u32>, Error>; fn block_number(&self, hash: Block::Hash) -> Result<Option<NumberFor<Block>>, Error>;
} }
impl<B, E, Block: BlockT<Hash=H256>, RA> BlockStatus<Block> for Arc<Client<B, E, Block, RA>> where impl<B, E, Block: BlockT<Hash=H256>, RA> BlockStatus<Block> for Arc<Client<B, E, Block, RA>> where
B: Backend<Block, Blake2Hasher>, B: Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync, E: CallExecutor<Block, Blake2Hasher> + Send + Sync,
RA: Send + Sync, RA: Send + Sync,
NumberFor<Block>: As<u32>, NumberFor<Block>: BlockNumberOps,
{ {
fn block_number(&self, hash: Block::Hash) -> Result<Option<u32>, Error> { fn block_number(&self, hash: Block::Hash) -> Result<Option<NumberFor<Block>>, Error> {
self.block_number_from_id(&BlockId::Hash(hash)) self.block_number_from_id(&BlockId::Hash(hash))
.map_err(|e| Error::Blockchain(format!("{:?}", e))) .map_err(|e| Error::Blockchain(format!("{:?}", e)))
.map(|num| num.map(|n| n.as_()))
} }
} }
/// Buffering imported messages until blocks with given hashes are imported. /// Buffering imported messages until blocks with given hashes are imported.
pub struct UntilImported<Block: BlockT, Status, I> { struct UntilImported<Block: BlockT, Status, I> {
import_notifications: Fuse<ImportNotifications<Block>>, import_notifications: Fuse<ImportNotifications<Block>>,
status_check: Status, status_check: Status,
inner: Fuse<I>, inner: Fuse<I>,
@@ -270,6 +344,7 @@ impl<Block: BlockT, Status: BlockStatus<Block>, I> Stream for UntilImported<Bloc
// clears the network messages for inner round on drop. // clears the network messages for inner round on drop.
struct ClearOnDrop<I, N: Network> { struct ClearOnDrop<I, N: Network> {
round: u64, round: u64,
set_id: u64,
inner: I, inner: I,
network: N, network: N,
} }
@@ -293,13 +368,27 @@ impl<I: Sink, N: Network> Sink for ClearOnDrop<I, N> {
impl<I, N: Network> Drop for ClearOnDrop<I, N> { impl<I, N: Network> Drop for ClearOnDrop<I, N> {
fn drop(&mut self) { fn drop(&mut self) {
self.network.drop_messages(self.round); self.network.drop_messages(self.round, self.set_id);
} }
} }
fn localized_payload<E: Encode>(round: u64, set_id: u64, message: &E) -> Vec<u8> {
let mut v = message.encode();
round.using_encoded(|s| v.extend(s));
set_id.using_encoded(|s| v.extend(s));
v
}
// converts a message stream into a stream of signed messages. // converts a message stream into a stream of signed messages.
// the output stream checks signatures also. // the output stream checks signatures also.
fn checked_message_stream<Block: BlockT, S>(inner: S, voters: Vec<AuthorityId>) fn checked_message_stream<Block: BlockT, S>(
round: u64,
set_id: u64,
inner: S,
voters: Arc<HashMap<AuthorityId, u64>>,
)
-> impl Stream<Item=SignedMessage<Block>,Error=Error> where -> impl Stream<Item=SignedMessage<Block>,Error=Error> where
S: Stream<Item=Vec<u8>,Error=()> S: Stream<Item=Vec<u8>,Error=()>
{ {
@@ -313,13 +402,13 @@ fn checked_message_stream<Block: BlockT, S>(inner: S, voters: Vec<AuthorityId>)
}) })
.and_then(move |msg| { .and_then(move |msg| {
// check signature. // check signature.
if !voters.contains(&msg.id) { if !voters.contains_key(&msg.id) {
debug!(target: "afg", "Skipping message from unknown voter {}", msg.id); debug!(target: "afg", "Skipping message from unknown voter {}", msg.id);
return Ok(None); return Ok(None);
} }
let as_public = ::ed25519::Public::from_raw(msg.id.0); let as_public = ::ed25519::Public::from_raw(msg.id.0);
let encoded_raw = msg.message.encode(); let encoded_raw = localized_payload(round, set_id, &msg.message);
if ::ed25519::verify_strong(&msg.signature, &encoded_raw, as_public) { if ::ed25519::verify_strong(&msg.signature, &encoded_raw, as_public) {
Ok(Some(msg)) Ok(Some(msg))
} else { } else {
@@ -332,9 +421,10 @@ fn checked_message_stream<Block: BlockT, S>(inner: S, voters: Vec<AuthorityId>)
} }
fn outgoing_messages<Block: BlockT, N: Network>( fn outgoing_messages<Block: BlockT, N: Network>(
local_key: Option<Arc<ed25519::Pair>>,
voters: Vec<AuthorityId>,
round: u64, round: u64,
set_id: u64,
local_key: Option<Arc<ed25519::Pair>>,
voters: Arc<HashMap<AuthorityId, u64>>,
network: N, network: N,
) -> ( ) -> (
impl Stream<Item=SignedMessage<Block>,Error=Error>, impl Stream<Item=SignedMessage<Block>,Error=Error>,
@@ -342,15 +432,20 @@ fn outgoing_messages<Block: BlockT, N: Network>(
) { ) {
let locals = local_key.and_then(|pair| { let locals = local_key.and_then(|pair| {
let public = pair.public(); let public = pair.public();
voters.iter().find(|id| id.0 == public.0).map(move |id| (pair, id.clone())) let id = AuthorityId(public.0);
if voters.contains_key(&id) {
Some((pair, id))
} else {
None
}
}); });
let (tx, rx) = mpsc::unbounded(); let (tx, rx) = mpsc::unbounded();
let rx = rx let rx = rx
.map(move |msg: Message<Block>| { .map(move |msg: Message<Block>| {
// when locals exist. sign messages on import // when locals exist, sign messages on import
if let Some((ref pair, local_id)) = locals { if let Some((ref pair, local_id)) = locals {
let encoded = msg.encode(); let encoded = localized_payload(round, set_id, &msg);
let signature = pair.sign(&encoded[..]); let signature = pair.sign(&encoded[..]);
let signed = SignedMessage::<Block> { let signed = SignedMessage::<Block> {
message: msg, message: msg,
@@ -359,7 +454,7 @@ fn outgoing_messages<Block: BlockT, N: Network>(
}; };
// forward to network. // forward to network.
network.send_message(round, signed.encode()); network.send_message(round, set_id, signed.encode());
Some(signed) Some(signed)
} else { } else {
None None
@@ -377,20 +472,22 @@ fn outgoing_messages<Block: BlockT, N: Network>(
} }
/// The environment we run GRANDPA in. /// The environment we run GRANDPA in.
pub struct Environment<B, E, Block: BlockT, N: Network, RA> { struct Environment<B, E, Block: BlockT, N: Network, RA> {
inner: Arc<Client<B, E, Block, RA>>, inner: Arc<Client<B, E, Block, RA>>,
voters: HashMap<AuthorityId, usize>, voters: Arc<HashMap<AuthorityId, u64>>,
config: Config, config: Config,
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
network: N, network: N,
set_id: u64,
} }
impl<Block: BlockT<Hash=H256>, B, E, N, RA> grandpa::Chain<Block::Hash> for Environment<B, E, Block, N, RA> where impl<Block: BlockT<Hash=H256>, B, E, N, RA> grandpa::Chain<Block::Hash, NumberFor<Block>> for Environment<B, E, Block, N, RA> where
Block: 'static, Block: 'static,
B: Backend<Block, Blake2Hasher> + 'static, B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static, E: CallExecutor<Block, Blake2Hasher> + 'static,
N: Network + 'static, N: Network + 'static,
N::In: 'static, N::In: 'static,
NumberFor<Block>: As<u32>, NumberFor<Block>: BlockNumberOps,
{ {
fn ancestry(&self, base: Block::Hash, block: Block::Hash) -> Result<Vec<Block::Hash>, GrandpaError> { fn ancestry(&self, base: Block::Hash, block: Block::Hash) -> Result<Vec<Block::Hash>, GrandpaError> {
if base == block { return Err(NotDescendent) } if base == block { return Err(NotDescendent) }
@@ -420,14 +517,23 @@ impl<Block: BlockT<Hash=H256>, B, E, N, RA> grandpa::Chain<Block::Hash> for Envi
Ok(tree_route.retracted().iter().skip(1).map(|e| e.hash).collect()) Ok(tree_route.retracted().iter().skip(1).map(|e| e.hash).collect())
} }
fn best_chain_containing(&self, block: Block::Hash) -> Option<(Block::Hash, u32)> { fn best_chain_containing(&self, block: Block::Hash) -> Option<(Block::Hash, NumberFor<Block>)> {
match self.inner.best_containing(block, None) { // we refuse to vote beyond the current limit number where transitions are scheduled to
// occur.
// once blocks are finalized that make that transition irrelevant or activate it,
// we will proceed onwards. most of the time there will be no pending transition.
let limit = self.authority_set.current_limit();
trace!(target: "afg", "Finding best chain containing block {:?} with number limit {:?}", block, limit);
match self.inner.best_containing(block, limit) {
Ok(Some(hash)) => { Ok(Some(hash)) => {
let header = self.inner.header(&BlockId::Hash(hash)).ok()? let header = self.inner.header(&BlockId::Hash(hash)).ok()?
.expect("Header known to exist after `best_containing` call; qed"); .expect("Header known to exist after `best_containing` call; qed");
Some((hash, header.number().as_())) Some((hash, header.number().clone()))
} }
// Ok(None) can be returned when `block` is after `limit`. That might cause issues.
// might be better to return the header itself in this (rare) case.
Ok(None) => None, Ok(None) => None,
Err(e) => { Err(e) => {
debug!(target: "afg", "Encountered error finding best chain containing {:?}: {:?}", block, e); debug!(target: "afg", "Encountered error finding best chain containing {:?}: {:?}", block, e);
@@ -437,22 +543,65 @@ impl<Block: BlockT<Hash=H256>, B, E, N, RA> grandpa::Chain<Block::Hash> for Envi
} }
} }
impl<B, E, Block: BlockT<Hash=H256>, N, RA> voter::Environment<Block::Hash> for Environment<B, E, Block, N, RA> where /// A new authority set along with the canonical block it changed at.
#[derive(Debug)]
struct NewAuthoritySet<H, N> {
canon_number: N,
canon_hash: H,
set_id: u64,
authorities: Vec<(AuthorityId, u64)>,
}
/// Signals either an early exit of a voter or an error.
#[derive(Debug)]
enum ExitOrError<H, N> {
/// An error occurred.
Error(Error),
/// Early exit of the voter: the new set ID and the new authorities along with respective weights.
AuthoritiesChanged(NewAuthoritySet<H, N>),
}
impl<H, N> From<Error> for ExitOrError<H, N> {
fn from(e: Error) -> Self {
ExitOrError::Error(e)
}
}
impl<H, N> From<ClientError> for ExitOrError<H, N> {
fn from(e: ClientError) -> Self {
ExitOrError::Error(Error::Client(e))
}
}
impl<H, N> From<grandpa::Error> for ExitOrError<H, N> {
fn from(e: grandpa::Error) -> Self {
ExitOrError::Error(Error::from(e))
}
}
impl<B, E, Block: BlockT<Hash=H256>, N, RA> voter::Environment<Block::Hash, NumberFor<Block>> for Environment<B, E, Block, N, RA> where
Block: 'static, Block: 'static,
B: Backend<Block, Blake2Hasher> + 'static, B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Send + Sync, E: CallExecutor<Block, Blake2Hasher> + 'static + Send + Sync,
N: Network + 'static, N: Network + 'static,
N::In: 'static, N::In: 'static,
RA: 'static + Send + Sync, RA: 'static + Send + Sync,
NumberFor<Block>: As<u32>, NumberFor<Block>: BlockNumberOps,
{ {
type Timer = Box<Future<Item = (), Error = Self::Error>>; type Timer = Box<Future<Item = (), Error = Self::Error>>;
type Id = AuthorityId; type Id = AuthorityId;
type Signature = ed25519::Signature; type Signature = ed25519::Signature;
type In = Box<Stream<Item = ::grandpa::SignedMessage<Block::Hash, Self::Signature, Self::Id>, Error = Self::Error>>; type In = Box<Stream<
type Out = Box<Sink<SinkItem = ::grandpa::Message<Block::Hash>, SinkError = Self::Error>>; Item = ::grandpa::SignedMessage<Block::Hash, NumberFor<Block>, Self::Signature, Self::Id>,
type Error = Error; Error = Self::Error,
>>;
type Out = Box<Sink<
SinkItem = ::grandpa::Message<Block::Hash, NumberFor<Block>>,
SinkError = Self::Error,
>>;
type Error = ExitOrError<Block::Hash, NumberFor<Block>>;
#[allow(unreachable_code)]
fn round_data( fn round_data(
&self, &self,
round: u64 round: u64
@@ -466,14 +615,17 @@ impl<B, E, Block: BlockT<Hash=H256>, N, RA> voter::Environment<Block::Hash> for
// TODO: dispatch this with `mpsc::spawn`. // TODO: dispatch this with `mpsc::spawn`.
let incoming = checked_message_stream::<Block, _>( let incoming = checked_message_stream::<Block, _>(
self.network.messages_for(round), round,
self.config.voters.clone(), self.set_id,
self.network.messages_for(round, self.set_id),
self.voters.clone(),
); );
let (out_rx, outgoing) = outgoing_messages::<Block, _>( let (out_rx, outgoing) = outgoing_messages::<Block, _>(
self.config.local_key.clone(),
self.config.voters.clone(),
round, round,
self.set_id,
self.config.local_key.clone(),
self.voters.clone(),
self.network.clone(), self.network.clone(),
); );
@@ -486,51 +638,121 @@ impl<B, E, Block: BlockT<Hash=H256>, N, RA> voter::Environment<Block::Hash> for
); );
// join incoming network messages with locally originating ones. // join incoming network messages with locally originating ones.
let incoming = Box::new(incoming.select(out_rx)); let incoming = Box::new(out_rx.select(incoming).map_err(Into::into));
// schedule network message cleanup when sink drops. // schedule network message cleanup when sink drops.
let outgoing = Box::new(ClearOnDrop { let outgoing = Box::new(ClearOnDrop {
round, round,
set_id: self.set_id,
network: self.network.clone(), network: self.network.clone(),
inner: outgoing, inner: outgoing.sink_map_err(Into::into),
}); });
voter::RoundData { voter::RoundData {
prevote_timer: Box::new(prevote_timer.map_err(Error::Timer)), prevote_timer: Box::new(prevote_timer.map_err(|e| Error::Timer(e).into())),
precommit_timer: Box::new(precommit_timer.map_err(Error::Timer)), precommit_timer: Box::new(precommit_timer.map_err(|e| Error::Timer(e).into())),
voters: self.voters.clone(), voters: (&*self.voters).clone(),
incoming, incoming,
outgoing, outgoing,
} }
} }
fn completed(&self, round: u64, state: RoundState<Block::Hash>) -> Result<(), Self::Error> { fn completed(&self, round: u64, state: RoundState<Block::Hash, NumberFor<Block>>) -> Result<(), Self::Error> {
debug!(
target: "afg", "Voter {} completed round {} in set {}. Estimate = {:?}, Finalized in round = {:?}",
self.config.name(),
round,
self.set_id,
state.estimate.as_ref().map(|e| e.1),
state.finalized.as_ref().map(|e| e.1),
);
let encoded_state = (round, state).encode(); let encoded_state = (round, state).encode();
if let Err(e) = self.inner.backend() if let Err(e) = self.inner.backend()
.insert_aux(&[(LAST_COMPLETED_KEY, &encoded_state[..])], &[]) .insert_aux(&[(LAST_COMPLETED_KEY, &encoded_state[..])], &[])
{ {
warn!(target: "afg", "Shutting down voter due to error bookkeeping last completed round in DB: {:?}", e); warn!(target: "afg", "Shutting down voter due to error bookkeeping last completed round in DB: {:?}", e);
Err(Error::CouldNotCompleteRound(e)) Err(Error::Client(e).into())
} else { } else {
Ok(()) Ok(())
} }
} }
fn finalize_block(&self, hash: Block::Hash, number: u32) -> Result<(), Self::Error> { fn finalize_block(&self, hash: Block::Hash, number: NumberFor<Block>) -> Result<(), Self::Error> {
// TODO: don't unconditionally notify. // ideally some handle to a synchronization oracle would be used
// to avoid unconditionally notifying.
if let Err(e) = self.inner.finalize_block(BlockId::Hash(hash), true) { if let Err(e) = self.inner.finalize_block(BlockId::Hash(hash), true) {
warn!(target: "afg", "Error applying finality to block {:?}: {:?}", (hash, number), e); warn!(target: "afg", "Error applying finality to block {:?}: {:?}", (hash, number), e);
// we return without error because not being able to finalize (temporarily) is
// non-fatal.
return Ok(());
} }
// we return without error in all cases because not being able to finalize is // lock must be held through writing to DB to avoid race
// non-fatal. let mut authority_set = self.authority_set.inner().write();
Ok(()) let client = &self.inner;
let status = authority_set.apply_changes(number, |canon_number| {
client.block_hash_from_id(&BlockId::number(canon_number))
.map(|h| h.expect("given number always less than newly-finalized number; \
thus there is a block with that number finalized already; qed"))
})?;
if status.changed {
// write new authority set state to disk.
let encoded_set = authority_set.encode();
let write_result = if let Some((ref canon_hash, ref canon_number)) = status.new_set_block {
// we also overwrite the "last completed round" entry with a blank slate
// because from the perspective of the finality gadget, the chain has
// reset.
let round_state = RoundState::genesis((*canon_hash, *canon_number));
let last_completed: LastCompleted<_, _> = (0, round_state);
let encoded = last_completed.encode();
client.backend().insert_aux(
&[
(AUTHORITY_SET_KEY, &encoded_set[..]),
(LAST_COMPLETED_KEY, &encoded[..]),
],
&[]
)
} else {
client.backend().insert_aux(&[(AUTHORITY_SET_KEY, &encoded_set[..])], &[])
};
if let Err(e) = write_result {
warn!(target: "finality", "Failed to write updated authority set to disk. Bailing.");
warn!(target: "finality", "Node is in a potentially inconsistent state.");
return Err(e.into());
}
}
if let Some((canon_hash, canon_number)) = status.new_set_block {
// the authority set has changed.
let (new_id, set_ref) = authority_set.current();
if set_ref.len() > 16 {
info!("Applying GRANDPA set change to new set with {} authorities", set_ref.len());
} else {
info!("Applying GRANDPA set change to new set {:?}", set_ref);
}
Err(ExitOrError::AuthoritiesChanged(NewAuthoritySet {
canon_hash,
canon_number,
set_id: new_id,
authorities: set_ref.to_vec(),
}))
} else {
Ok(())
}
} }
fn prevote_equivocation( fn prevote_equivocation(
&self, &self,
_round: u64, _round: u64,
equivocation: ::grandpa::Equivocation<Self::Id, Prevote<Block::Hash>, Self::Signature> equivocation: ::grandpa::Equivocation<Self::Id, Prevote<Block>, Self::Signature>
) { ) {
warn!(target: "afg", "Detected prevote equivocation in the finality worker: {:?}", equivocation); warn!(target: "afg", "Detected prevote equivocation in the finality worker: {:?}", equivocation);
// nothing yet; this could craft misbehavior reports of some kind. // nothing yet; this could craft misbehavior reports of some kind.
@@ -539,232 +761,209 @@ impl<B, E, Block: BlockT<Hash=H256>, N, RA> voter::Environment<Block::Hash> for
fn precommit_equivocation( fn precommit_equivocation(
&self, &self,
_round: u64, _round: u64,
equivocation: Equivocation<Self::Id, Precommit<Block::Hash>, Self::Signature> equivocation: Equivocation<Self::Id, Precommit<Block>, Self::Signature>
) { ) {
warn!(target: "afg", "Detected precommit equivocation in the finality worker: {:?}", equivocation); warn!(target: "afg", "Detected precommit equivocation in the finality worker: {:?}", equivocation);
// nothing yet // nothing yet
} }
} }
/// Run a GRANDPA voter as a task. The returned future should be executed in a tokio runtime.
pub fn run_grandpa<B, E: Send + Sync, Block: BlockT<Hash=H256>, N, RA: Send + Sync + 'static>(
config: Config, /// A block-import handler for GRANDPA.
///
/// This scans each imported block for signals of changing authority set.
/// When using GRANDPA, the block import worker should be using this block import
/// object.
pub struct GrandpaBlockImport<B, E, Block: BlockT<Hash=H256>, RA> {
inner: Arc<Client<B, E, Block, RA>>,
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
}
impl<B, E, Block: BlockT<Hash=H256>, RA> BlockImport<Block> for GrandpaBlockImport<B, E, Block, RA> where
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
DigestFor<Block>: Encode,
RA: GrandpaApi<Block> + TaggedTransactionQueue<Block>,
{
type Error = ClientError;
fn import_block(&self, mut block: ImportBlock<Block>, new_authorities: Option<Vec<AuthorityId>>)
-> Result<ImportResult, Self::Error>
{
use authorities::PendingChange;
let maybe_change = self.inner.runtime_api().grandpa_pending_change(
&BlockId::hash(*block.header.parent_hash()),
&block.header.digest().clone(),
)?;
// when we update the authorities, we need to hold the lock
// until the block is written to prevent a race if we need to restore
// the old authority set on error.
let just_in_case = maybe_change.map(|change| {
let hash = block.header.hash();
let number = block.header.number().clone();
let mut authorities = self.authority_set.inner().write();
let old_set = authorities.clone();
authorities.add_pending_change(PendingChange {
next_authorities: change.next_authorities,
finalization_depth: change.delay,
canon_height: number,
canon_hash: hash,
});
block.auxiliary.push((AUTHORITY_SET_KEY.to_vec(), Some(authorities.encode())));
(old_set, authorities)
});
let result = self.inner.import_block(block, new_authorities);
if let Err(ref e) = result {
if let Some((old_set, mut authorities)) = just_in_case {
debug!(target: "afg", "Restoring old set after block import error: {:?}", e);
*authorities = old_set;
}
}
result
}
}
/// Half of a link between a block-import worker and a the background voter.
// This should remain non-clone.
pub struct LinkHalf<B, E, Block: BlockT<Hash=H256>, RA> {
client: Arc<Client<B, E, Block, RA>>, client: Arc<Client<B, E, Block, RA>>,
voters: HashMap<AuthorityId, usize>, authority_set: SharedAuthoritySet<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>(client: Arc<Client<B, E, Block, RA>>)
-> Result<(GrandpaBlockImport<B, E, Block, RA>, LinkHalf<B, E, Block, RA>), ClientError>
where
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
RA: GrandpaApi<Block>,
{
use runtime_primitives::traits::Zero;
let authority_set = match client.backend().get_aux(AUTHORITY_SET_KEY)? {
None => {
info!(target: "afg", "Loading GRANDPA authorities \
from genesis on what appears to be first startup.");
// no authority set on disk: fetch authorities from genesis state.
// if genesis state is not available, we may be a light client, but these
// are unsupported for following GRANDPA directly.
let genesis_authorities = client.runtime_api()
.grandpa_authorities(&BlockId::number(Zero::zero()))?;
let authority_set = SharedAuthoritySet::genesis(genesis_authorities);
let encoded = authority_set.inner().read().encode();
client.backend().insert_aux(&[(AUTHORITY_SET_KEY, &encoded[..])], &[])?;
authority_set
}
Some(raw) => ::authorities::AuthoritySet::decode(&mut &raw[..])
.ok_or_else(|| ::client::error::ErrorKind::Backend(
format!("GRANDPA authority set kept in invalid format")
))?
.into(),
};
Ok((
GrandpaBlockImport { inner: client.clone(), authority_set: authority_set.clone() },
LinkHalf { client, authority_set },
))
}
/// 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<B, E, Block: BlockT<Hash=H256>, N, RA>(
config: Config,
link: LinkHalf<B, E, Block, RA>,
network: N, network: N,
) -> Result<impl Future<Item=(),Error=()>,client::error::Error> where ) -> ::client::error::Result<impl Future<Item=(),Error=()>> where
Block::Hash: Ord, Block::Hash: Ord,
B: Backend<Block, Blake2Hasher> + 'static, B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + 'static, E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
N: Network + 'static, N: Network + 'static,
N::In: 'static, N::In: 'static,
NumberFor<Block>: As<u32>, NumberFor<Block>: BlockNumberOps,
DigestFor<Block>: Encode,
RA: Send + Sync + 'static,
{ {
use futures::future::{self, Loop as FutureLoop};
use runtime_primitives::traits::Zero;
let LinkHalf { client, authority_set } = link;
let chain_info = client.info()?; let chain_info = client.info()?;
let genesis_hash = chain_info.chain.genesis_hash; let genesis_hash = chain_info.chain.genesis_hash;
let last_finalized = (
chain_info.chain.finalized_hash,
chain_info.chain.finalized_number.as_()
);
let (last_round_number, last_state) = match client.backend().get_aux(LAST_COMPLETED_KEY)? { let (last_round_number, last_state) = match client.backend().get_aux(LAST_COMPLETED_KEY)? {
None => (0, RoundState::genesis((genesis_hash, 0))), None => (0, RoundState::genesis((genesis_hash, <NumberFor<Block>>::zero()))),
Some(raw) => <(u64, RoundState<Block::Hash>)>::decode(&mut &raw[..]) Some(raw) => LastCompleted::decode(&mut &raw[..])
.ok_or_else(|| ::client::error::ErrorKind::Backend( .ok_or_else(|| ::client::error::ErrorKind::Backend(
format!("Last GRANDPA round state kept in invalid format") format!("Last GRANDPA round state kept in invalid format")
))? ))?
}; };
let environment = Arc::new(Environment { let voters = authority_set.inner().read().current().1.iter()
inner: client, .cloned()
config, .collect();
voters,
network, let initial_environment = Arc::new(Environment {
inner: client.clone(),
config: config.clone(),
voters: Arc::new(voters),
network: network.clone(),
set_id: authority_set.set_id(),
authority_set: authority_set.clone(),
}); });
let voter = voter::Voter::new( let work = future::loop_fn((initial_environment, last_round_number, last_state), move |params| {
environment, let (env, last_round_number, last_state) = params;
last_round_number, debug!(target: "afg", "{}: Starting new voter with set ID {}", config.name(), env.set_id);
last_state,
last_finalized,
);
Ok(voter.map_err(|e| warn!("GRANDPA Voter failed: {:?}", e))) let chain_info = match client.info() {
} Ok(i) => i,
Err(e) => return future::Either::B(future::err(Error::Client(e))),
};
#[cfg(test)] let last_finalized = (
mod tests { chain_info.chain.finalized_hash,
use super::*; chain_info.chain.finalized_number,
use network::test::*; );
use parking_lot::Mutex;
use tokio::runtime::current_thread;
use keyring::Keyring;
use client::BlockchainEvents;
#[derive(Clone)] let voter = voter::Voter::new(env, last_round_number, last_state, last_finalized);
struct TestGrandpaNetwork { let client = client.clone();
inner: Arc<Mutex<TestNet>>, let config = config.clone();
peer_id: usize, let network = network.clone();
} let authority_set = authority_set.clone();
future::Either::A(voter.then(move |res| match res {
// voters don't conclude naturally; this could reasonably be an error.
Ok(()) => Ok(FutureLoop::Break(())),
Err(ExitOrError::Error(e)) => Err(e),
Err(ExitOrError::AuthoritiesChanged(new)) => {
let env = Arc::new(Environment {
inner: client,
config,
voters: Arc::new(new.authorities.into_iter().collect()),
set_id: new.set_id,
network,
authority_set,
});
impl TestGrandpaNetwork { // start the new authority set using the block where the
fn new(inner: Arc<Mutex<TestNet>>, peer_id: usize,) -> Self { // set changed (not where the signal happened!) as the base.
TestGrandpaNetwork { Ok(FutureLoop::Continue((
inner, env,
peer_id, 0, // always start at round 0 when changing sets.
RoundState::genesis((new.canon_hash, new.canon_number)),
)))
} }
} }))
} });
fn round_to_topic(round: u64) -> Hash { Ok(work.map_err(|e| warn!("GRANDPA Voter failed: {:?}", e)))
let mut hash = Hash::default();
round.using_encoded(|s| {
let raw = hash.as_mut();
raw[..8].copy_from_slice(s);
});
hash
}
impl Network for TestGrandpaNetwork {
type In = Box<Stream<Item=Vec<u8>,Error=()>>;
fn messages_for(&self, round: u64) -> Self::In {
let messages = self.inner.lock().peer(self.peer_id)
.with_spec(|spec, _| spec.gossip.messages_for(round_to_topic(round)));
let messages = messages.map_err(
move |_| panic!("Messages for round {} dropped too early", round)
);
Box::new(messages)
}
fn send_message(&self, round: u64, message: Vec<u8>) {
let mut inner = self.inner.lock();
inner.peer(self.peer_id).gossip_message(round_to_topic(round), message);
inner.route();
}
fn drop_messages(&self, round: u64) {
let topic = round_to_topic(round);
self.inner.lock().peer(self.peer_id)
.with_spec(|spec, _| spec.gossip.collect_garbage(|t| t == &topic));
}
}
const TEST_GOSSIP_DURATION: Duration = Duration::from_millis(500);
const TEST_ROUTING_INTERVAL: Duration = Duration::from_millis(50);
#[test]
fn finalize_20_unanimous_3_peers() {
let mut net = TestNet::new(3);
net.peer(0).push_blocks(20, false);
net.sync();
let net = Arc::new(Mutex::new(net));
let peers = &[
(0, Keyring::Alice),
(1, Keyring::Bob),
(2, Keyring::Charlie),
];
let voters: Vec<_> = peers.iter()
.map(|&(_, ref key)| AuthorityId(key.to_raw_public()))
.collect();
let mut finality_notifications = Vec::new();
let mut runtime = current_thread::Runtime::new().unwrap();
for (peer_id, key) in peers {
let client = net.lock().peer(*peer_id).client().clone();
finality_notifications.push(
client.finality_notification_stream()
.take_while(|n| Ok(n.header.number() < &20))
.for_each(move |_| Ok(()))
);
let voter = run_grandpa(
Config {
gossip_duration: TEST_GOSSIP_DURATION,
voters: voters.clone(),
local_key: Some(Arc::new(key.clone().into())),
},
client,
voters.iter().map(|&id| (id, 1)).collect(),
TestGrandpaNetwork::new(net.clone(), *peer_id),
).expect("all in order with client and network");
runtime.spawn(voter);
}
// wait for all finalized on each.
let wait_for = ::futures::future::join_all(finality_notifications)
.map(|_| ())
.map_err(|_| ());
let drive_to_completion = ::tokio::timer::Interval::new_interval(TEST_ROUTING_INTERVAL)
.for_each(move |_| { net.lock().route_until_complete(); Ok(()) })
.map(|_| ())
.map_err(|_| ());
runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap();
}
#[test]
fn observer_can_finalize() {
let mut net = TestNet::new(4);
net.peer(0).push_blocks(20, false);
net.sync();
let net = Arc::new(Mutex::new(net));
let peers = &[
(0, Keyring::Alice),
(1, Keyring::Bob),
(2, Keyring::Charlie),
];
let voters: HashMap<_, _> = peers.iter()
.map(|&(_, ref key)| (AuthorityId(key.to_raw_public()), 1))
.collect();
let mut finality_notifications = Vec::new();
let mut runtime = current_thread::Runtime::new().unwrap();
let all_peers = peers.iter()
.cloned()
.map(|(id, key)| (id, Some(Arc::new(key.into()))))
.chain(::std::iter::once((3, None)));
for (peer_id, local_key) in all_peers {
let client = net.lock().peer(peer_id).client().clone();
finality_notifications.push(
client.finality_notification_stream()
.take_while(|n| Ok(n.header.number() < &20))
.for_each(move |_| Ok(()))
);
let voter = run_grandpa(
Config {
gossip_duration: TEST_GOSSIP_DURATION,
voters: voters.keys().cloned().collect(),
local_key,
},
client,
voters.clone(),
TestGrandpaNetwork::new(net.clone(), peer_id),
).expect("all in order with client and network");
runtime.spawn(voter);
}
// wait for all finalized on each.
let wait_for = ::futures::future::join_all(finality_notifications)
.map(|_| ())
.map_err(|_| ());
let drive_to_completion = ::tokio::timer::Interval::new_interval(TEST_ROUTING_INTERVAL)
.for_each(move |_| { net.lock().route_until_complete(); Ok(()) })
.map(|_| ())
.map_err(|_| ());
runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap();
}
} }
@@ -0,0 +1,460 @@
// Copyright 2018 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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 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. If not, see <http://www.gnu.org/licenses/>.
//! Tests and test helpers for GRANDPA.
use super::*;
use network::test::{Block, Hash, TestNetFactory, Peer, PeersClient};
use network::import_queue::{PassThroughVerifier};
use network::config::{ProtocolConfig, Roles};
use parking_lot::Mutex;
use tokio::runtime::current_thread;
use keyring::Keyring;
use client::BlockchainEvents;
use test_client::{self, runtime::BlockNumber};
use codec::Decode;
use consensus_common::BlockOrigin;
use std::collections::HashSet;
use authorities::AuthoritySet;
type PeerData = Mutex<Option<LinkHalf<test_client::Backend, test_client::Executor, Block, test_client::runtime::ClientWithApi>>>;
type GrandpaPeer = Peer<PassThroughVerifier, PeerData>;
struct GrandpaTestNet {
peers: Vec<Arc<GrandpaPeer>>,
test_config: TestApi,
started: bool
}
impl GrandpaTestNet {
fn new(test_config: TestApi, n_peers: usize) -> Self {
let mut net = GrandpaTestNet {
peers: Vec::with_capacity(n_peers),
started: false,
test_config,
};
let config = Self::default_config();
for _ in 0..n_peers {
net.add_peer(&config);
}
net
}
}
impl TestNetFactory for GrandpaTestNet {
type Verifier = PassThroughVerifier;
type PeerData = PeerData;
/// Create new test network with peers and given config.
fn from_config(_config: &ProtocolConfig) -> Self {
GrandpaTestNet {
peers: Vec::new(),
test_config: Default::default(),
started: false
}
}
fn default_config() -> ProtocolConfig {
// the authority role ensures gossip hits all nodes here.
ProtocolConfig {
roles: Roles::AUTHORITY,
}
}
fn make_verifier(&self, _client: Arc<PeersClient>, _cfg: &ProtocolConfig)
-> Arc<Self::Verifier>
{
Arc::new(PassThroughVerifier(false)) // use non-instant finality.
}
fn make_block_import(&self, client: Arc<PeersClient>)
-> (Arc<BlockImport<Block,Error=ClientError> + Send + Sync>, PeerData)
{
let (import, link) = block_import(client, self.test_config.clone()).expect("Could not create block import for fresh peer.");
(Arc::new(import), Mutex::new(Some(link)))
}
fn peer(&self, i: usize) -> &GrandpaPeer {
&self.peers[i]
}
fn peers(&self) -> &Vec<Arc<GrandpaPeer>> {
&self.peers
}
fn mut_peers<F: Fn(&mut Vec<Arc<GrandpaPeer>>)>(&mut self, closure: F) {
closure(&mut self.peers);
}
fn started(&self) -> bool {
self.started
}
fn set_started(&mut self, new: bool) {
self.started = new;
}
}
#[derive(Clone)]
struct MessageRouting {
inner: Arc<Mutex<GrandpaTestNet>>,
peer_id: usize,
}
impl MessageRouting {
fn new(inner: Arc<Mutex<GrandpaTestNet>>, peer_id: usize,) -> Self {
MessageRouting {
inner,
peer_id,
}
}
}
fn make_topic(round: u64, set_id: u64) -> Hash {
let mut hash = Hash::default();
round.using_encoded(|s| {
let raw = hash.as_mut();
raw[..8].copy_from_slice(s);
});
set_id.using_encoded(|s| {
let raw = hash.as_mut();
raw[8..16].copy_from_slice(s);
});
hash
}
impl Network for MessageRouting {
type In = Box<Stream<Item=Vec<u8>,Error=()>>;
fn messages_for(&self, round: u64, set_id: u64) -> Self::In {
let messages = self.inner.lock().peer(self.peer_id)
.with_spec(|spec, _| spec.gossip.messages_for(make_topic(round, set_id)));
let messages = messages.map_err(
move |_| panic!("Messages for round {} dropped too early", round)
);
Box::new(messages)
}
fn send_message(&self, round: u64, set_id: u64, message: Vec<u8>) {
let mut inner = self.inner.lock();
inner.peer(self.peer_id).gossip_message(make_topic(round, set_id), message);
inner.route_until_complete();
}
fn drop_messages(&self, round: u64, set_id: u64) {
let topic = make_topic(round, set_id);
self.inner.lock().peer(self.peer_id)
.with_spec(|spec, _| spec.gossip.collect_garbage(|t| t == &topic));
}
}
#[derive(Default, Clone)]
struct TestApi {
genesis_authorities: Vec<(AuthorityId, u64)>,
scheduled_changes: Arc<Mutex<HashMap<Hash, ScheduledChange<BlockNumber>>>>,
}
impl TestApi {
fn new(genesis_authorities: Vec<(AuthorityId, u64)>) -> Self {
TestApi {
genesis_authorities,
scheduled_changes: Arc::new(Mutex::new(HashMap::new())),
}
}
}
impl ApiClient<Block> for TestApi {
fn genesis_authorities(&self) -> Result<Vec<(AuthorityId, u64)>, ClientError> {
Ok(self.genesis_authorities.clone())
}
fn scheduled_change(&self, header: &<Block as BlockT>::Header)
-> Result<Option<ScheduledChange<NumberFor<Block>>>, ClientError>
{
// we take only scheduled changes at given block number where there are no
// extrinsics.
Ok(self.scheduled_changes.lock().get(&header.hash()).map(|c| c.clone()))
}
}
const TEST_GOSSIP_DURATION: Duration = Duration::from_millis(500);
const TEST_ROUTING_INTERVAL: Duration = Duration::from_millis(50);
fn make_ids(keys: &[Keyring]) -> Vec<(AuthorityId, u64)> {
keys.iter()
.map(|key| AuthorityId(key.to_raw_public()))
.map(|id| (id, 1))
.collect()
}
#[test]
fn finalize_3_voters_no_observers() {
let peers = &[Keyring::Alice, Keyring::Bob, Keyring::Charlie];
let voters = make_ids(peers);
let mut net = GrandpaTestNet::new(TestApi::new(voters), 3);
net.peer(0).push_blocks(20, false);
net.sync();
for i in 0..3 {
assert_eq!(net.peer(i).client().info().unwrap().chain.best_number, 20,
"Peer #{} failed to sync", i);
}
let net = Arc::new(Mutex::new(net));
let mut finality_notifications = Vec::new();
let mut runtime = current_thread::Runtime::new().unwrap();
for (peer_id, key) in peers.iter().enumerate() {
let (client, link) = {
let mut net = net.lock();
// temporary needed for some reason
let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed");
(
net.peers[peer_id].client().clone(),
link,
)
};
finality_notifications.push(
client.finality_notification_stream()
.take_while(|n| Ok(n.header.number() < &20))
.for_each(|_| Ok(()))
);
let voter = run_grandpa(
Config {
gossip_duration: TEST_GOSSIP_DURATION,
local_key: Some(Arc::new(key.clone().into())),
name: Some(format!("peer#{}", peer_id)),
},
link,
MessageRouting::new(net.clone(), peer_id),
).expect("all in order with client and network");
runtime.spawn(voter);
}
// wait for all finalized on each.
let wait_for = ::futures::future::join_all(finality_notifications)
.map(|_| ())
.map_err(|_| ());
let drive_to_completion = ::tokio::timer::Interval::new_interval(TEST_ROUTING_INTERVAL)
.for_each(move |_| { net.lock().route_until_complete(); Ok(()) })
.map(|_| ())
.map_err(|_| ());
runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap();
}
#[test]
fn finalize_3_voters_1_observer() {
let peers = &[Keyring::Alice, Keyring::Bob, Keyring::Charlie];
let voters = make_ids(peers);
let mut net = GrandpaTestNet::new(TestApi::new(voters), 4);
net.peer(0).push_blocks(20, false);
net.sync();
let net = Arc::new(Mutex::new(net));
let mut finality_notifications = Vec::new();
let mut runtime = current_thread::Runtime::new().unwrap();
let all_peers = peers.iter()
.cloned()
.map(|key| Some(Arc::new(key.into())))
.chain(::std::iter::once(None));
for (peer_id, local_key) in all_peers.enumerate() {
let (client, link) = {
let mut net = net.lock();
let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed");
(
net.peers[peer_id].client().clone(),
link,
)
};
finality_notifications.push(
client.finality_notification_stream()
.take_while(|n| Ok(n.header.number() < &20))
.for_each(move |_| Ok(()))
);
let voter = run_grandpa(
Config {
gossip_duration: TEST_GOSSIP_DURATION,
local_key,
name: Some(format!("peer#{}", peer_id)),
},
link,
MessageRouting::new(net.clone(), peer_id),
).expect("all in order with client and network");
runtime.spawn(voter);
}
// wait for all finalized on each.
let wait_for = ::futures::future::join_all(finality_notifications)
.map(|_| ())
.map_err(|_| ());
let drive_to_completion = ::tokio::timer::Interval::new_interval(TEST_ROUTING_INTERVAL)
.for_each(move |_| { net.lock().route_until_complete(); Ok(()) })
.map(|_| ())
.map_err(|_| ());
runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap();
}
#[test]
fn transition_3_voters_twice_1_observer() {
let peers_a = &[
Keyring::Alice,
Keyring::Bob,
Keyring::Charlie,
];
let peers_b = &[
Keyring::Dave,
Keyring::Eve,
Keyring::Ferdie,
];
let peers_c = &[
Keyring::Alice,
Keyring::Eve,
Keyring::Two,
];
let observer = &[Keyring::One];
let genesis_voters = make_ids(peers_a);
let api = TestApi::new(genesis_voters);
let transitions = api.scheduled_changes.clone();
let add_transition = move |hash, change| {
transitions.lock().insert(hash, change);
};
let mut net = GrandpaTestNet::new(api, 9);
// first 20 blocks: transition at 15, applied at 20.
{
net.peer(0).push_blocks(14, false);
net.peer(0).generate_blocks(1, BlockOrigin::File, |builder| {
let block = builder.bake().unwrap();
add_transition(block.header.hash(), ScheduledChange {
next_authorities: make_ids(peers_b),
delay: 4,
});
block
});
net.peer(0).push_blocks(5, false);
}
// at block 21 we do another transition, but this time instant.
// add more until we have 30.
{
net.peer(0).generate_blocks(1, BlockOrigin::File, |builder| {
let block = builder.bake().unwrap();
add_transition(block.header.hash(), ScheduledChange {
next_authorities: make_ids(peers_c),
delay: 0,
});
block
});
net.peer(0).push_blocks(9, false);
}
net.sync();
for (i, peer) in net.peers().iter().enumerate() {
assert_eq!(peer.client().info().unwrap().chain.best_number, 30,
"Peer #{} failed to sync", i);
let set_raw = peer.client().backend().get_aux(::AUTHORITY_SET_KEY).unwrap().unwrap();
let set = AuthoritySet::<Hash, BlockNumber>::decode(&mut &set_raw[..]).unwrap();
assert_eq!(set.current(), (0, make_ids(peers_a).as_slice()));
assert_eq!(set.pending_changes().len(), 2);
}
let net = Arc::new(Mutex::new(net));
let mut finality_notifications = Vec::new();
let mut runtime = current_thread::Runtime::new().unwrap();
let all_peers = peers_a.iter()
.chain(peers_b)
.chain(peers_c)
.chain(observer)
.cloned()
.collect::<HashSet<_>>() // deduplicate
.into_iter()
.map(|key| Some(Arc::new(key.into())))
.enumerate();
for (peer_id, local_key) in all_peers {
let (client, link) = {
let mut net = net.lock();
let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed");
(
net.peers[peer_id].client().clone(),
link,
)
};
finality_notifications.push(
client.finality_notification_stream()
.take_while(|n| Ok(n.header.number() < &30))
.for_each(move |_| Ok(()))
.map(move |()| {
let set_raw = client.backend().get_aux(::AUTHORITY_SET_KEY).unwrap().unwrap();
let set = AuthoritySet::<Hash, BlockNumber>::decode(&mut &set_raw[..]).unwrap();
assert_eq!(set.current(), (2, make_ids(peers_c).as_slice()));
assert!(set.pending_changes().is_empty());
})
);
let voter = run_grandpa(
Config {
gossip_duration: TEST_GOSSIP_DURATION,
local_key,
name: Some(format!("peer#{}", peer_id)),
},
link,
MessageRouting::new(net.clone(), peer_id),
).expect("all in order with client and network");
runtime.spawn(voter);
}
// wait for all finalized on each.
let wait_for = ::futures::future::join_all(finality_notifications)
.map(|_| ())
.map_err(|_| ());
let drive_to_completion = ::tokio::timer::Interval::new_interval(TEST_ROUTING_INTERVAL)
.for_each(move |_| { net.lock().route_until_complete(); Ok(()) })
.map(|_| ())
.map_err(|_| ());
runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap();
}
+164 -142
View File
@@ -34,14 +34,16 @@ use primitives::AuthorityId;
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}; use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero};
pub use blocks::BlockData; pub use blocks::BlockData;
use chain::Client; use client::error::Error as ClientError;
use error::{ErrorKind, Error}; use error::{ErrorKind, Error};
use protocol::Context; use protocol::Context;
use service::ExecuteInContext; use service::ExecuteInContext;
use sync::ChainSync; use sync::ChainSync;
pub use consensus::{ImportBlock, ImportResult, BlockOrigin}; pub use consensus::{ImportBlock, BlockImport, ImportResult, BlockOrigin};
/// Shared block import struct used by the queue.
pub type SharedBlockImport<B> = Arc<dyn BlockImport<B,Error=ClientError> + Send + Sync>;
#[cfg(any(test, feature = "test-helpers"))] #[cfg(any(test, feature = "test-helpers"))]
use std::cell::RefCell; use std::cell::RefCell;
@@ -66,14 +68,9 @@ pub trait ImportQueue<B: BlockT>: Send + Sync {
/// ///
/// This is called automatically by the network service when synchronization /// This is called automatically by the network service when synchronization
/// begins. /// begins.
fn start<E>( fn start<L>(&self, _link: L) -> Result<(), Error> where
&self,
_sync: Weak<RwLock<ChainSync<B>>>,
_service: Weak<E>,
_chain: Weak<Client<B>>
) -> Result<(), Error> where
Self: Sized, Self: Sized,
E: 'static + ExecuteInContext<B>, L: 'static + Link<B>,
{ {
Ok(()) Ok(())
} }
@@ -103,6 +100,7 @@ pub struct BasicQueue<B: BlockT, V: 'static + Verifier<B>> {
handle: Mutex<Option<::std::thread::JoinHandle<()>>>, handle: Mutex<Option<::std::thread::JoinHandle<()>>>,
data: Arc<AsyncImportQueueData<B>>, data: Arc<AsyncImportQueueData<B>>,
verifier: Arc<V>, verifier: Arc<V>,
block_import: SharedBlockImport<B>,
} }
/// Locks order: queue, queue_blocks, best_importing_number /// Locks order: queue, queue_blocks, best_importing_number
@@ -116,11 +114,12 @@ struct AsyncImportQueueData<B: BlockT> {
impl<B: BlockT, V: Verifier<B>> BasicQueue<B, V> { impl<B: BlockT, V: Verifier<B>> BasicQueue<B, V> {
/// Instantiate a new basic queue, with given verifier. /// Instantiate a new basic queue, with given verifier.
pub fn new(verifier: Arc<V>) -> Self { pub fn new(verifier: Arc<V>, block_import: SharedBlockImport<B>) -> Self {
Self { Self {
handle: Mutex::new(None), handle: Mutex::new(None),
data: Arc::new(AsyncImportQueueData::new()), data: Arc::new(AsyncImportQueueData::new()),
verifier, verifier,
block_import,
} }
} }
} }
@@ -138,18 +137,17 @@ impl<B: BlockT> AsyncImportQueueData<B> {
} }
impl<B: BlockT, V: 'static + Verifier<B>> ImportQueue<B> for BasicQueue<B, V> { impl<B: BlockT, V: 'static + Verifier<B>> ImportQueue<B> for BasicQueue<B, V> {
fn start<E: 'static + ExecuteInContext<B>>( fn start<L: 'static + Link<B>>(
&self, &self,
sync: Weak<RwLock<ChainSync<B>>>, link: L,
service: Weak<E>,
chain: Weak<Client<B>>
) -> Result<(), Error> { ) -> Result<(), Error> {
debug_assert!(self.handle.lock().is_none()); debug_assert!(self.handle.lock().is_none());
let qdata = self.data.clone(); let qdata = self.data.clone();
let verifier = self.verifier.clone(); let verifier = self.verifier.clone();
let block_import = self.block_import.clone();
*self.handle.lock() = Some(::std::thread::Builder::new().name("ImportQueue".into()).spawn(move || { *self.handle.lock() = Some(::std::thread::Builder::new().name("ImportQueue".into()).spawn(move || {
import_thread(sync, service, chain, qdata, verifier) import_thread(block_import, link, qdata, verifier)
}).map_err(|err| Error::from(ErrorKind::Io(err)))?); }).map_err(|err| Error::from(ErrorKind::Io(err)))?);
Ok(()) Ok(())
} }
@@ -215,10 +213,9 @@ impl<B: BlockT, V: 'static + Verifier<B>> Drop for BasicQueue<B, V> {
} }
/// Blocks import thread. /// Blocks import thread.
fn import_thread<B: BlockT, E: ExecuteInContext<B>, V: Verifier<B>>( fn import_thread<B: BlockT, L: Link<B>, V: Verifier<B>>(
sync: Weak<RwLock<ChainSync<B>>>, block_import: SharedBlockImport<B>,
service: Weak<E>, link: L,
chain: Weak<Client<B>>,
qdata: Arc<AsyncImportQueueData<B>>, qdata: Arc<AsyncImportQueueData<B>>,
verifier: Arc<V> verifier: Arc<V>
) { ) {
@@ -243,91 +240,87 @@ fn import_thread<B: BlockT, E: ExecuteInContext<B>, V: Verifier<B>>(
} }
}; };
match (sync.upgrade(), service.upgrade(), chain.upgrade()) { let blocks_hashes: Vec<B::Hash> = new_blocks.1.iter().map(|b| b.block.hash.clone()).collect();
(Some(sync), Some(service), Some(chain)) => { if !import_many_blocks(
let blocks_hashes: Vec<B::Hash> = new_blocks.1.iter().map(|b| b.block.hash.clone()).collect(); &*block_import,
if !import_many_blocks( &link,
&mut SyncLink{chain: &sync, client: &*chain, context: &*service}, Some(&*qdata),
Some(&*qdata), new_blocks,
new_blocks, verifier.clone(),
verifier.clone(), ) {
) { break;
break; }
}
let mut queue_blocks = qdata.queue_blocks.write(); let mut queue_blocks = qdata.queue_blocks.write();
for blocks_hash in blocks_hashes { for blocks_hash in blocks_hashes {
queue_blocks.remove(&blocks_hash); queue_blocks.remove(&blocks_hash);
}
},
_ => break,
} }
} }
trace!(target: "sync", "Stopping import thread"); trace!(target: "sync", "Stopping import thread");
} }
/// ChainSync link trait.
trait SyncLinkApi<B: BlockT> { /// Hooks that the verification queue can use to influence the synchronization
/// Get chain reference. /// algorithm.
fn chain(&self) -> &Client<B>; pub trait Link<B: BlockT>: Send {
/// Block imported. /// Block imported.
fn block_imported(&mut self, hash: &B::Hash, number: NumberFor<B>); fn block_imported(&self, _hash: &B::Hash, _number: NumberFor<B>) { }
/// Maintain sync. /// Maintain sync.
fn maintain_sync(&mut self); fn maintain_sync(&self) { }
/// Disconnect from peer. /// Disconnect from peer.
fn useless_peer(&mut self, who: NodeIndex, reason: &str); fn useless_peer(&self, _who: NodeIndex, _reason: &str) { }
/// Disconnect from peer and restart sync. /// Disconnect from peer and restart sync.
fn note_useless_and_restart_sync(&mut self, who: NodeIndex, reason: &str); fn note_useless_and_restart_sync(&self, _who: NodeIndex, _reason: &str) { }
/// Restart sync. /// Restart sync.
fn restart(&mut self); fn restart(&self) { }
} }
/// A link implementation that does nothing.
pub struct NoopLink;
/// Link with the ChainSync service. impl<B: BlockT> Link<B> for NoopLink { }
struct SyncLink<'a, B: 'a + BlockT, E: 'a + ExecuteInContext<B>> {
pub chain: &'a RwLock<ChainSync<B>>, /// A link implementation that connects to the network.
pub client: &'a Client<B>, pub struct NetworkLink<B: BlockT, E: ExecuteInContext<B>> {
pub context: &'a E, /// The chain-sync handle
pub(crate) sync: Weak<RwLock<ChainSync<B>>>,
/// Network context.
pub(crate) context: Weak<E>,
} }
impl<'a, B: 'static + BlockT, E: 'a + ExecuteInContext<B>> SyncLink<'a, B, E> { impl<B: BlockT, E: ExecuteInContext<B>> NetworkLink<B, E> {
/// Execute closure with locked ChainSync. /// Execute closure with locked ChainSync.
fn with_sync<F: Fn(&mut ChainSync<B>, &mut Context<B>)>(&mut self, closure: F) { fn with_sync<F: Fn(&mut ChainSync<B>, &mut Context<B>)>(&self, closure: F) {
let service = self.context; if let (Some(sync), Some(service)) = (self.sync.upgrade(), self.context.upgrade()) {
let sync = self.chain; service.execute_in_context(move |protocol| {
service.execute_in_context(move |protocol| { let mut sync = sync.write();
let mut sync = sync.write(); closure(&mut *sync, protocol)
closure(&mut *sync, protocol) });
}); }
} }
} }
impl<'a, B: 'static + BlockT, E: 'a + ExecuteInContext<B>> SyncLinkApi<B> for SyncLink<'a, B, E> { impl<B: BlockT, E: ExecuteInContext<B>> Link<B> for NetworkLink<B, E> {
fn block_imported(&self, hash: &B::Hash, number: NumberFor<B>) {
fn chain(&self) -> &Client<B> {
self.client
}
fn block_imported(&mut self, hash: &B::Hash, number: NumberFor<B>) {
self.with_sync(|sync, _| sync.block_imported(&hash, number)) self.with_sync(|sync, _| sync.block_imported(&hash, number))
} }
fn maintain_sync(&mut self) { fn maintain_sync(&self) {
self.with_sync(|sync, protocol| sync.maintain_sync(protocol)) self.with_sync(|sync, protocol| sync.maintain_sync(protocol))
} }
fn useless_peer(&mut self, who: NodeIndex, reason: &str) { fn useless_peer(&self, who: NodeIndex, reason: &str) {
self.with_sync(|_, protocol| protocol.report_peer(who, Severity::Useless(reason))) self.with_sync(|_, protocol| protocol.report_peer(who, Severity::Useless(reason)))
} }
fn note_useless_and_restart_sync(&mut self, who: NodeIndex, reason: &str) { fn note_useless_and_restart_sync(&self, who: NodeIndex, reason: &str) {
self.with_sync(|sync, protocol| { self.with_sync(|sync, protocol| {
protocol.report_peer(who, Severity::Useless(reason)); // is this actually malign or just useless? protocol.report_peer(who, Severity::Useless(reason)); // is this actually malign or just useless?
sync.restart(protocol); sync.restart(protocol);
}) })
} }
fn restart(&mut self) { fn restart(&self) {
self.with_sync(|sync, protocol| sync.restart(protocol)) self.with_sync(|sync, protocol| sync.restart(protocol))
} }
} }
@@ -360,7 +353,8 @@ enum BlockImportError {
/// Import a bunch of blocks. /// Import a bunch of blocks.
fn import_many_blocks<'a, B: BlockT, V: Verifier<B>>( fn import_many_blocks<'a, B: BlockT, V: Verifier<B>>(
link: &mut SyncLinkApi<B>, import_handle: &BlockImport<B, Error=ClientError>,
link: &Link<B>,
qdata: Option<&AsyncImportQueueData<B>>, qdata: Option<&AsyncImportQueueData<B>>,
blocks: (BlockOrigin, Vec<BlockData<B>>), blocks: (BlockOrigin, Vec<BlockData<B>>),
verifier: Arc<V> verifier: Arc<V>
@@ -383,7 +377,7 @@ fn import_many_blocks<'a, B: BlockT, V: Verifier<B>>(
// Blocks in the response/drain should be in ascending order. // Blocks in the response/drain should be in ascending order.
for block in blocks { for block in blocks {
let import_result = import_single_block( let import_result = import_single_block(
link.chain(), import_handle,
blocks_origin.clone(), blocks_origin.clone(),
block, block,
verifier.clone(), verifier.clone(),
@@ -407,7 +401,7 @@ fn import_many_blocks<'a, B: BlockT, V: Verifier<B>>(
/// Single block import function. /// Single block import function.
fn import_single_block<B: BlockT, V: Verifier<B>>( fn import_single_block<B: BlockT, V: Verifier<B>>(
chain: &Client<B>, import_handle: &BlockImport<B,Error=ClientError>,
block_origin: BlockOrigin, block_origin: BlockOrigin,
block: BlockData<B>, block: BlockData<B>,
verifier: Arc<V> verifier: Arc<V>
@@ -449,7 +443,7 @@ fn import_single_block<B: BlockT, V: Verifier<B>>(
BlockImportError::VerificationFailed(peer, msg) BlockImportError::VerificationFailed(peer, msg)
})?; })?;
match chain.import(import_block, new_authorities) { match import_handle.import_block(import_block, new_authorities) {
Ok(ImportResult::AlreadyInChain) => { Ok(ImportResult::AlreadyInChain) => {
trace!(target: "sync", "Block already in chain {}: {:?}", number, hash); trace!(target: "sync", "Block already in chain {}: {:?}", number, hash);
Ok(BlockImportResult::ImportedKnown(hash, number)) Ok(BlockImportResult::ImportedKnown(hash, number))
@@ -478,8 +472,8 @@ fn import_single_block<B: BlockT, V: Verifier<B>>(
} }
/// Process single block import result. /// Process single block import result.
fn process_import_result<'a, B: BlockT>( fn process_import_result<B: BlockT>(
link: &mut SyncLinkApi<B>, link: &Link<B>,
result: Result<BlockImportResult<B::Hash, <<B as BlockT>::Header as HeaderT>::Number>, BlockImportError> result: Result<BlockImportResult<B::Hash, <<B as BlockT>::Header as HeaderT>::Number>, BlockImportError>
) -> usize ) -> usize
{ {
@@ -576,40 +570,61 @@ impl<B: BlockT> Verifier<B> for PassThroughVerifier {
} }
} }
#[cfg(any(test, feature = "test-helpers"))]
/// Blocks import queue that is importing blocks in the same thread. /// Blocks import queue that is importing blocks in the same thread.
/// The boolean value indicates whether blocks should be imported without instant finality. /// The boolean value indicates whether blocks should be imported without instant finality.
pub struct SyncImportQueue<B: BlockT, V: Verifier<B>>(Arc<V>, ImportCB<B>);
#[cfg(any(test, feature = "test-helpers"))] #[cfg(any(test, feature = "test-helpers"))]
impl<B: BlockT, V: Verifier<B>> SyncImportQueue<B, V> { pub struct SyncImportQueue<B: BlockT, V: Verifier<B>> {
/// Create a new SyncImportQueue wrapping the given Verifier verifier: Arc<V>,
pub fn new(verifier: Arc<V>) -> Self { link: ImportCB<B>,
SyncImportQueue(verifier, ImportCB::new()) block_import: SharedBlockImport<B>,
}
#[cfg(any(test, feature = "test-helpers"))]
impl<B: 'static + BlockT, V: 'static + Verifier<B>> SyncImportQueue<B, V> {
/// Create a new SyncImportQueue wrapping the given Verifier and block import
/// handle.
pub fn new(verifier: Arc<V>, block_import: SharedBlockImport<B>) -> Self {
let queue = SyncImportQueue {
verifier,
link: ImportCB::new(),
block_import,
};
let v = queue.verifier.clone();
let import_handle = queue.block_import.clone();
queue.link.set(Box::new(move |origin, new_blocks| {
let verifier = v.clone();
import_many_blocks(
&*import_handle,
&NoopLink,
None,
(origin, new_blocks),
verifier,
)
}));
queue
} }
} }
#[cfg(any(test, feature = "test-helpers"))] #[cfg(any(test, feature = "test-helpers"))]
impl<B: 'static + BlockT, V: 'static + Verifier<B>> ImportQueue<B> for SyncImportQueue<B, V> impl<B: 'static + BlockT, V: 'static + Verifier<B>> ImportQueue<B> for SyncImportQueue<B, V>
{ {
fn start<E: 'static + ExecuteInContext<B>>( fn start<L: 'static + Link<B>>(
&self, &self,
sync: Weak<RwLock<ChainSync<B>>>, link: L,
service: Weak<E>,
chain: Weak<Client<B>>
) -> Result<(), Error> { ) -> Result<(), Error> {
let v = self.0.clone(); let v = self.verifier.clone();
self.1.set(Box::new(move | origin, new_blocks | { let import_handle = self.block_import.clone();
self.link.set(Box::new(move |origin, new_blocks| {
let verifier = v.clone(); let verifier = v.clone();
match (sync.upgrade(), service.upgrade(), chain.upgrade()) { import_many_blocks(
(Some(sync), Some(service), Some(chain)) => &*import_handle,
import_many_blocks( &link,
&mut SyncLink{chain: &sync, client: &*chain, context: &*service}, None,
None, (origin, new_blocks),
(origin, new_blocks), verifier,
verifier, )
),
_ => false
}
})); }));
Ok(()) Ok(())
} }
@@ -629,7 +644,7 @@ impl<B: 'static + BlockT, V: 'static + Verifier<B>> ImportQueue<B> for SyncImpor
} }
fn import_blocks(&self, origin: BlockOrigin, blocks: Vec<BlockData<B>>) { fn import_blocks(&self, origin: BlockOrigin, blocks: Vec<BlockData<B>>) {
self.1.call(origin, blocks); self.link.call(origin, blocks);
} }
} }
@@ -639,42 +654,49 @@ pub mod tests {
use message; use message;
use test_client::{self, TestClient}; use test_client::{self, TestClient};
use test_client::runtime::{Block, Hash}; use test_client::runtime::{Block, Hash};
use on_demand::tests::DummyExecutor;
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use std::cell::Cell;
use super::*; use super::*;
struct TestLink { struct TestLink {
chain: Arc<Client<Block>>, imported: Cell<usize>,
imported: usize, maintains: Cell<usize>,
maintains: usize, disconnects: Cell<usize>,
disconnects: usize, restarts: Cell<usize>,
restarts: usize,
} }
impl TestLink { impl TestLink {
fn new() -> TestLink { fn new() -> TestLink {
TestLink { TestLink {
chain: Arc::new(test_client::new()), imported: Cell::new(0),
imported: 0, maintains: Cell::new(0),
maintains: 0, disconnects: Cell::new(0),
disconnects: 0, restarts: Cell::new(0),
restarts: 0,
} }
} }
fn total(&self) -> usize { fn total(&self) -> usize {
self.imported + self.maintains + self.disconnects + self.restarts self.imported.get() + self.maintains.get() + self.disconnects.get() + self.restarts.get()
} }
} }
impl SyncLinkApi<Block> for TestLink { impl Link<Block> for TestLink {
fn chain(&self) -> &Client<Block> { &*self.chain } fn block_imported(&self, _hash: &Hash, _number: NumberFor<Block>) {
fn block_imported(&mut self, _hash: &Hash, _number: NumberFor<Block>) { self.imported += 1; } self.imported.set(self.imported.get() + 1);
fn maintain_sync(&mut self) { self.maintains += 1; } }
fn useless_peer(&mut self, _: NodeIndex, _: &str) { self.disconnects += 1; } fn maintain_sync(&self) {
fn note_useless_and_restart_sync(&mut self, _: NodeIndex, _: &str) { self.disconnects += 1; self.restarts += 1; } self.maintains.set(self.maintains.get() + 1);
fn restart(&mut self) { self.restarts += 1; } }
fn useless_peer(&self, _: NodeIndex, _: &str) {
self.disconnects.set(self.disconnects.get() + 1);
}
fn note_useless_and_restart_sync(&self, id: NodeIndex, r: &str) {
self.useless_peer(id, r);
self.restart();
}
fn restart(&self) {
self.restarts.set(self.restarts.get() + 1);
}
} }
fn prepare_good_block() -> (client::Client<test_client::Backend, test_client::Executor, Block, test_client::runtime::ClientWithApi>, Hash, u64, BlockData<Block>) { fn prepare_good_block() -> (client::Client<test_client::Backend, test_client::Executor, Block, test_client::runtime::ClientWithApi>, Hash, u64, BlockData<Block>) {
@@ -735,39 +757,39 @@ pub mod tests {
#[test] #[test]
fn process_import_result_works() { fn process_import_result_works() {
let mut link = TestLink::new(); let link = TestLink::new();
assert_eq!(process_import_result::<Block>(&mut link, Ok(BlockImportResult::ImportedKnown(Default::default(), 0))), 1); assert_eq!(process_import_result::<Block>(&link, Ok(BlockImportResult::ImportedKnown(Default::default(), 0))), 1);
assert_eq!(link.total(), 1); assert_eq!(link.total(), 1);
let mut link = TestLink::new(); let link = TestLink::new();
assert_eq!(process_import_result::<Block>(&mut link, Ok(BlockImportResult::ImportedKnown(Default::default(), 0))), 1); assert_eq!(process_import_result::<Block>(&link, Ok(BlockImportResult::ImportedKnown(Default::default(), 0))), 1);
assert_eq!(link.total(), 1); assert_eq!(link.total(), 1);
assert_eq!(link.imported, 1); assert_eq!(link.imported.get(), 1);
let mut link = TestLink::new(); let link = TestLink::new();
assert_eq!(process_import_result::<Block>(&mut link, Ok(BlockImportResult::ImportedUnknown(Default::default(), 0))), 1); assert_eq!(process_import_result::<Block>(&link, Ok(BlockImportResult::ImportedUnknown(Default::default(), 0))), 1);
assert_eq!(link.total(), 1); assert_eq!(link.total(), 1);
assert_eq!(link.imported, 1); assert_eq!(link.imported.get(), 1);
let mut link = TestLink::new(); let link = TestLink::new();
assert_eq!(process_import_result::<Block>(&mut link, Err(BlockImportError::IncompleteHeader(Some(0)))), 0); assert_eq!(process_import_result::<Block>(&link, Err(BlockImportError::IncompleteHeader(Some(0)))), 0);
assert_eq!(link.total(), 1); assert_eq!(link.total(), 1);
assert_eq!(link.disconnects, 1); assert_eq!(link.disconnects.get(), 1);
let mut link = TestLink::new(); let link = TestLink::new();
assert_eq!(process_import_result::<Block>(&mut link, Err(BlockImportError::IncompleteJustification(Some(0)))), 0); assert_eq!(process_import_result::<Block>(&link, Err(BlockImportError::IncompleteJustification(Some(0)))), 0);
assert_eq!(link.total(), 1); assert_eq!(link.total(), 1);
assert_eq!(link.disconnects, 1); assert_eq!(link.disconnects.get(), 1);
let mut link = TestLink::new(); let link = TestLink::new();
assert_eq!(process_import_result::<Block>(&mut link, Err(BlockImportError::UnknownParent)), 0); assert_eq!(process_import_result::<Block>(&link, Err(BlockImportError::UnknownParent)), 0);
assert_eq!(link.total(), 1); assert_eq!(link.total(), 1);
assert_eq!(link.restarts, 1); assert_eq!(link.restarts.get(), 1);
let mut link = TestLink::new(); let link = TestLink::new();
assert_eq!(process_import_result::<Block>(&mut link, Err(BlockImportError::Error)), 0); assert_eq!(process_import_result::<Block>(&link, Err(BlockImportError::Error)), 0);
assert_eq!(link.total(), 1); assert_eq!(link.total(), 1);
assert_eq!(link.restarts, 1); assert_eq!(link.restarts.get(), 1);
} }
#[test] #[test]
@@ -776,7 +798,9 @@ pub mod tests {
let qdata = AsyncImportQueueData::new(); let qdata = AsyncImportQueueData::new();
let verifier = Arc::new(PassThroughVerifier(true)); let verifier = Arc::new(PassThroughVerifier(true));
qdata.is_stopping.store(true, Ordering::SeqCst); qdata.is_stopping.store(true, Ordering::SeqCst);
let client = test_client::new();
assert!(!import_many_blocks( assert!(!import_many_blocks(
&client,
&mut TestLink::new(), &mut TestLink::new(),
Some(&qdata), Some(&qdata),
(BlockOrigin::File, vec![block.clone(), block]), (BlockOrigin::File, vec![block.clone(), block]),
@@ -789,10 +813,8 @@ pub mod tests {
// Perform this test multiple times since it exhibits non-deterministic behavior. // Perform this test multiple times since it exhibits non-deterministic behavior.
for _ in 0..100 { for _ in 0..100 {
let verifier = Arc::new(PassThroughVerifier(true)); let verifier = Arc::new(PassThroughVerifier(true));
let queue = BasicQueue::new(verifier); let queue = BasicQueue::new(verifier, Arc::new(test_client::new()));
let service = Arc::new(DummyExecutor); queue.start(TestLink::new()).unwrap();
let chain = Arc::new(test_client::new());
queue.start(Weak::new(), Arc::downgrade(&service), Arc::downgrade(&chain) as Weak<Client<Block>>).unwrap();
drop(queue); drop(queue);
} }
} }
+9 -8
View File
@@ -84,7 +84,6 @@ impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> Service<B, S,
protocol_id: ProtocolId, protocol_id: ProtocolId,
import_queue: I, import_queue: I,
) -> Result<Arc<Service<B, S, H>>, Error> { ) -> Result<Arc<Service<B, S, H>>, Error> {
let chain = params.chain.clone();
let import_queue = Arc::new(import_queue); let import_queue = Arc::new(import_queue);
let handler = Arc::new(Protocol::new( let handler = Arc::new(Protocol::new(
params.config, params.config,
@@ -98,20 +97,22 @@ impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> Service<B, S,
let registered = RegisteredProtocol::new(protocol_id, &versions[..]); let registered = RegisteredProtocol::new(protocol_id, &versions[..]);
let (thread, network) = start_thread(params.network_config, handler.clone(), registered)?; let (thread, network) = start_thread(params.network_config, handler.clone(), registered)?;
let sync = Arc::new(Service { let service = Arc::new(Service {
network, network,
protocol_id, protocol_id,
handler, handler,
bg_thread: Some(thread), bg_thread: Some(thread),
}); });
import_queue.start( // connect the import-queue to the network service.
Arc::downgrade(sync.handler.sync()), let link = ::import_queue::NetworkLink {
Arc::downgrade(&sync), sync: Arc::downgrade(service.handler.sync()),
Arc::downgrade(&chain) context: Arc::downgrade(&service),
)?; };
Ok(sync) import_queue.start(link)?;
Ok(service)
} }
/// Called when a new block is imported by the client. /// Called when a new block is imported by the client.
+64 -26
View File
@@ -24,6 +24,7 @@ use std::sync::Arc;
use parking_lot::RwLock; use parking_lot::RwLock;
use client; use client;
use client::error::Error as ClientError;
use client::block_builder::BlockBuilder; use client::block_builder::BlockBuilder;
use runtime_primitives::generic::BlockId; use runtime_primitives::generic::BlockId;
use io::SyncIo; use io::SyncIo;
@@ -37,7 +38,7 @@ use import_queue::{SyncImportQueue, PassThroughVerifier, Verifier};
use consensus::BlockOrigin; use consensus::BlockOrigin;
use specialization::NetworkSpecialization; use specialization::NetworkSpecialization;
use consensus_gossip::ConsensusGossip; use consensus_gossip::ConsensusGossip;
use import_queue::ImportQueue; use import_queue::{BlockImport, ImportQueue};
use service::ExecuteInContext; use service::ExecuteInContext;
use test_client; use test_client;
@@ -138,33 +139,38 @@ pub struct TestPacket {
pub type PeersClient = client::Client<test_client::Backend, test_client::Executor, Block, test_client::runtime::ClientWithApi>; pub type PeersClient = client::Client<test_client::Backend, test_client::Executor, Block, test_client::runtime::ClientWithApi>;
pub struct Peer<V: Verifier<Block>> { pub struct Peer<V: Verifier<Block>, D> {
client: Arc<PeersClient>, client: Arc<PeersClient>,
pub sync: Arc<Protocol<Block, DummySpecialization, Hash>>, pub sync: Arc<Protocol<Block, DummySpecialization, Hash>>,
pub queue: Arc<RwLock<VecDeque<TestPacket>>>, pub queue: Arc<RwLock<VecDeque<TestPacket>>>,
import_queue: Arc<SyncImportQueue<Block, V>>, import_queue: Arc<SyncImportQueue<Block, V>>,
executor: Arc<DummyContextExecutor>, executor: Arc<DummyContextExecutor>,
/// Some custom data set up at initialization time.
pub data: D,
} }
impl<V: 'static + Verifier<Block>> Peer<V> { impl<V: 'static + Verifier<Block>, D> Peer<V, D> {
fn new( fn new(
client: Arc<PeersClient>, client: Arc<PeersClient>,
sync: Arc<Protocol<Block, DummySpecialization, Hash>>, sync: Arc<Protocol<Block, DummySpecialization, Hash>>,
queue: Arc<RwLock<VecDeque<TestPacket>>>, queue: Arc<RwLock<VecDeque<TestPacket>>>,
import_queue: Arc<SyncImportQueue<Block, V>>, import_queue: Arc<SyncImportQueue<Block, V>>,
data: D,
) -> Self { ) -> Self {
let executor = Arc::new(DummyContextExecutor(sync.clone(), queue.clone())); let executor = Arc::new(DummyContextExecutor(sync.clone(), queue.clone()));
Peer { client, sync, queue, import_queue, executor} Peer { client, sync, queue, import_queue, executor, data }
} }
/// Called after blockchain has been populated to updated current state. /// Called after blockchain has been populated to updated current state.
fn start(&self) { fn start(&self) {
// Update the sync state to the latest chain state. // Update the sync state to the latest chain state.
let info = self.client.info().expect("In-mem client does not fail"); let info = self.client.info().expect("In-mem client does not fail");
let header = self.client.header(&BlockId::Hash(info.chain.best_hash)).unwrap().unwrap(); let header = self.client.header(&BlockId::Hash(info.chain.best_hash)).unwrap().unwrap();
self.import_queue.start( let network_link = ::import_queue::NetworkLink {
Arc::downgrade(&self.sync.sync()), sync: Arc::downgrade(self.sync.sync()),
Arc::downgrade(&self.executor), context: Arc::downgrade(&self.executor),
Arc::downgrade(&self.sync.context_data().chain)).expect("Test ImportQueue always starts"); };
self.import_queue.start(network_link).expect("Test ImportQueue always starts");
self.sync.on_block_imported(&mut TestIo::new(&self.queue, None), info.chain.best_hash, &header); self.sync.on_block_imported(&mut TestIo::new(&self.queue, None), info.chain.best_hash, &header);
} }
@@ -187,6 +193,11 @@ impl<V: 'static + Verifier<Block>> Peer<V> {
io.to_disconnect.clone() io.to_disconnect.clone()
} }
fn with_io<'a, F, U>(&'a self, f: F) -> U where F: FnOnce(&mut TestIo<'a>) -> U {
let mut io = TestIo::new(&self.queue, None);
f(&mut io)
}
/// Produce the next pending message to send to another peer. /// Produce the next pending message to send to another peer.
fn pending_message(&self) -> Option<TestPacket> { fn pending_message(&self) -> Option<TestPacket> {
self.flush(); self.flush();
@@ -229,25 +240,39 @@ impl<V: 'static + Verifier<Block>> Peer<V> {
/// Add blocks to the peer -- edit the block before adding /// Add blocks to the peer -- edit the block before adding
pub fn generate_blocks<F>(&self, count: usize, origin: BlockOrigin, mut edit_block: F) pub fn generate_blocks<F>(&self, count: usize, origin: BlockOrigin, mut edit_block: F)
where F: FnMut(&mut BlockBuilder<Block, PeersClient>) where F: FnMut(BlockBuilder<Block, PeersClient>) -> Block
{ {
for _ in 0 .. count { use blocks::BlockData;
let mut builder = self.client.new_block().unwrap();
edit_block(&mut builder); for _ in 0..count {
let block = builder.bake().unwrap(); let builder = self.client.new_block().unwrap();
let block = edit_block(builder);
let hash = block.header.hash(); let hash = block.header.hash();
trace!("Generating {}, (#{}, parent={})", hash, block.header.number, block.header.parent_hash); trace!("Generating {}, (#{}, parent={})", hash, block.header.number, block.header.parent_hash);
let header = block.header.clone(); let header = block.header.clone();
self.client.justify_and_import(origin, block).unwrap();
self.sync.on_block_imported(&mut TestIo::new(&self.queue, None), hash, &header); // NOTE: if we use a non-synchronous queue in the test-net in the future,
// this may not work.
self.import_queue.import_blocks(origin, vec![BlockData {
origin: None,
block: ::message::BlockData::<Block> {
hash,
header: Some(header),
body: Some(block.extrinsics),
receipt: None,
message_queue: None,
justification: Some(Vec::new()),
},
}]);
} }
} }
/// Push blocks to the peer (simplified: with or without a TX) /// Push blocks to the peer (simplified: with or without a TX)
pub fn push_blocks(&self, count: usize, with_tx: bool) { pub fn push_blocks(&self, count: usize, with_tx: bool) {
let mut nonce = 0; let mut nonce = 0;
if with_tx { if with_tx {
self.generate_blocks(count, BlockOrigin::File, |builder| { self.generate_blocks(count, BlockOrigin::File, |mut builder| {
let transfer = Transfer { let transfer = Transfer {
from: Keyring::Alice.to_raw_public().into(), from: Keyring::Alice.to_raw_public().into(),
to: Keyring::Alice.to_raw_public().into(), to: Keyring::Alice.to_raw_public().into(),
@@ -257,9 +282,10 @@ impl<V: 'static + Verifier<Block>> Peer<V> {
let signature = Keyring::from_raw_public(transfer.from.to_fixed_bytes()).unwrap().sign(&transfer.encode()).into(); let signature = Keyring::from_raw_public(transfer.from.to_fixed_bytes()).unwrap().sign(&transfer.encode()).into();
builder.push(Extrinsic { transfer, signature }).unwrap(); builder.push(Extrinsic { transfer, signature }).unwrap();
nonce = nonce + 1; nonce = nonce + 1;
builder.bake().unwrap()
}); });
} else { } else {
self.generate_blocks(count, BlockOrigin::File, |_| ()); self.generate_blocks(count, BlockOrigin::File, |builder| builder.bake().unwrap());
} }
} }
@@ -292,6 +318,7 @@ impl TransactionPool<Hash, Block> for EmptyTransactionPool {
pub trait TestNetFactory: Sized { pub trait TestNetFactory: Sized {
type Verifier: 'static + Verifier<Block>; type Verifier: 'static + Verifier<Block>;
type PeerData: Default;
/// These two need to be implemented! /// These two need to be implemented!
fn from_config(config: &ProtocolConfig) -> Self; fn from_config(config: &ProtocolConfig) -> Self;
@@ -299,13 +326,20 @@ pub trait TestNetFactory: Sized {
/// Get reference to peer. /// Get reference to peer.
fn peer(&self, i: usize) -> &Peer<Self::Verifier>; fn peer(&self, i: usize) -> &Peer<Self::Verifier, Self::PeerData>;
fn peers(&self) -> &Vec<Arc<Peer<Self::Verifier>>>; fn peers(&self) -> &Vec<Arc<Peer<Self::Verifier, Self::PeerData>>>;
fn mut_peers<F: Fn(&mut Vec<Arc<Peer<Self::Verifier>>>)>(&mut self, closure: F ); fn mut_peers<F: Fn(&mut Vec<Arc<Peer<Self::Verifier, Self::PeerData>>>)>(&mut self, closure: F);
fn started(&self) -> bool; fn started(&self) -> bool;
fn set_started(&mut self, now: bool); fn set_started(&mut self, now: bool);
/// Get custom block import handle for fresh client, along with peer data.
fn make_block_import(&self, client: Arc<PeersClient>)
-> (Arc<BlockImport<Block,Error=ClientError> + Send + Sync>, Self::PeerData)
{
(client, Default::default())
}
fn default_config() -> ProtocolConfig { fn default_config() -> ProtocolConfig {
ProtocolConfig::default() ProtocolConfig::default()
} }
@@ -326,7 +360,9 @@ pub trait TestNetFactory: Sized {
let client = Arc::new(test_client::new()); let client = Arc::new(test_client::new());
let tx_pool = Arc::new(EmptyTransactionPool); let tx_pool = Arc::new(EmptyTransactionPool);
let verifier = self.make_verifier(client.clone(), config); let verifier = self.make_verifier(client.clone(), config);
let import_queue = Arc::new(SyncImportQueue::new(verifier)); let (block_import, data) = self.make_block_import(client.clone());
let import_queue = Arc::new(SyncImportQueue::new(verifier, block_import));
let specialization = DummySpecialization { let specialization = DummySpecialization {
gossip: ConsensusGossip::new(), gossip: ConsensusGossip::new(),
}; };
@@ -343,7 +379,8 @@ pub trait TestNetFactory: Sized {
client, client,
Arc::new(sync), Arc::new(sync),
Arc::new(RwLock::new(VecDeque::new())), Arc::new(RwLock::new(VecDeque::new())),
import_queue import_queue,
data,
)); ));
self.mut_peers(|peers| { self.mut_peers(|peers| {
@@ -453,12 +490,13 @@ pub trait TestNetFactory: Sized {
} }
pub struct TestNet { pub struct TestNet {
peers: Vec<Arc<Peer<PassThroughVerifier>>>, peers: Vec<Arc<Peer<PassThroughVerifier, ()>>>,
started: bool started: bool
} }
impl TestNetFactory for TestNet { impl TestNetFactory for TestNet {
type Verifier = PassThroughVerifier; type Verifier = PassThroughVerifier;
type PeerData = ();
/// Create new test network with peers and given config. /// Create new test network with peers and given config.
fn from_config(_config: &ProtocolConfig) -> Self { fn from_config(_config: &ProtocolConfig) -> Self {
@@ -474,15 +512,15 @@ impl TestNetFactory for TestNet {
Arc::new(PassThroughVerifier(false)) Arc::new(PassThroughVerifier(false))
} }
fn peer(&self, i: usize) -> &Peer<Self::Verifier> { fn peer(&self, i: usize) -> &Peer<Self::Verifier, ()> {
&self.peers[i] &self.peers[i]
} }
fn peers(&self) -> &Vec<Arc<Peer<Self::Verifier>>> { fn peers(&self) -> &Vec<Arc<Peer<Self::Verifier, ()>>> {
&self.peers &self.peers
} }
fn mut_peers<F: Fn(&mut Vec<Arc<Peer<Self::Verifier>>>)>(&mut self, closure: F ) { fn mut_peers<F: Fn(&mut Vec<Arc<Peer<Self::Verifier, ()>>>)>(&mut self, closure: F ) {
closure(&mut self.peers); closure(&mut self.peers);
} }
+4 -1
View File
@@ -94,7 +94,10 @@ fn own_blocks_are_announced() {
::env_logger::init().ok(); ::env_logger::init().ok();
let mut net = TestNet::new(3); let mut net = TestNet::new(3);
net.sync(); // connect'em net.sync(); // connect'em
net.peer(0).generate_blocks(1, BlockOrigin::Own, |_| ()); net.peer(0).generate_blocks(1, BlockOrigin::Own, |builder| builder.bake().unwrap());
let header = net.peer(0).client().header(&BlockId::Number(1)).unwrap().unwrap();
net.peer(0).with_io(|io| net.peer(0).sync.on_block_imported(io, header.hash(), &header));
net.sync(); net.sync();
assert_eq!(net.peer(0).client.backend().blockchain().info().unwrap().best_number, 1); assert_eq!(net.peer(0).client.backend().blockchain().info().unwrap().best_number, 1);
assert_eq!(net.peer(1).client.backend().blockchain().info().unwrap().best_number, 1); assert_eq!(net.peer(1).client.backend().blockchain().info().unwrap().best_number, 1);
+6 -2
View File
@@ -21,7 +21,7 @@ use futures::Future;
use runtime_primitives::generic::{SignedBlock, BlockId}; use runtime_primitives::generic::{SignedBlock, BlockId};
use runtime_primitives::traits::{As, Block, Header}; use runtime_primitives::traits::{As, Block, Header};
use network::import_queue::{ImportQueue, BlockData}; use network::import_queue::{ImportQueue, Link, BlockData};
use network::message; use network::message;
use consensus_common::BlockOrigin; use consensus_common::BlockOrigin;
@@ -90,8 +90,12 @@ pub fn export_blocks<F, E, W>(config: FactoryFullConfiguration<F>, exit: E, mut
pub fn import_blocks<F, E, R>(config: FactoryFullConfiguration<F>, exit: E, mut input: R) -> error::Result<()> pub fn import_blocks<F, E, R>(config: FactoryFullConfiguration<F>, exit: E, mut input: R) -> error::Result<()>
where F: ServiceFactory, E: Future<Item=(),Error=()> + Send + 'static, R: Read, where F: ServiceFactory, E: Future<Item=(),Error=()> + Send + 'static, R: Read,
{ {
struct DummyLink;
impl<B: Block> Link<B> for DummyLink { }
let client = new_client::<F>(&config)?; let client = new_client::<F>(&config)?;
let queue = components::FullComponents::<F>::build_import_queue(&config, client.clone())?; let queue = components::FullComponents::<F>::build_import_queue(&config, client.clone())?;
queue.start(DummyLink)?;
let (exit_send, exit_recv) = std::sync::mpsc::channel(); let (exit_send, exit_recv) = std::sync::mpsc::channel();
::std::thread::spawn(move || { ::std::thread::spawn(move || {
@@ -101,7 +105,7 @@ pub fn import_blocks<F, E, R>(config: FactoryFullConfiguration<F>, exit: E, mut
let count: u32 = Decode::decode(&mut input).ok_or("Error reading file")?; let count: u32 = Decode::decode(&mut input).ok_or("Error reading file")?;
info!("Importing {} blocks", count); info!("Importing {} blocks", count);
let mut block_count = 0; let mut block_count = 0;
for b in 0 .. count { for b in 0 .. count {
if exit_recv.try_recv().is_ok() { if exit_recv.try_recv().is_ok() {
break; break;
+2 -2
View File
@@ -472,8 +472,8 @@ impl<C: Components> network::TransactionPool<ComponentExHash<C>, ComponentBlock<
/// // The first one is for the initializing the full import queue and the second for the /// // The first one is for the initializing the full import queue and the second for the
/// // light import queue. /// // light import queue.
/// ImportQueue = BasicQueue<Block, NoneVerifier> /// ImportQueue = BasicQueue<Block, NoneVerifier>
/// { |_, _| Ok(BasicQueue::new(Arc::new(NoneVerifier {}))) } /// { |_, client| Ok(BasicQueue::new(Arc::new(NoneVerifier {}, client))) }
/// { |_, _| Ok(BasicQueue::new(Arc::new(NoneVerifier {}))) }, /// { |_, client| Ok(BasicQueue::new(Arc::new(NoneVerifier {}, client))) },
/// } /// }
/// } /// }
/// ``` /// ```
-1
View File
@@ -33,7 +33,6 @@ extern crate sr_io as runtime_io;
#[macro_use] #[macro_use]
extern crate sr_version as runtime_version; extern crate sr_version as runtime_version;
#[cfg(test)] #[cfg(test)]
#[macro_use] #[macro_use]
extern crate hex_literal; extern crate hex_literal;
+4 -2
View File
@@ -14,7 +14,8 @@ parity-codec-derive = "2.1"
sr-std = { path = "../../core/sr-std" } sr-std = { path = "../../core/sr-std" }
srml-support = { path = "../../srml/support" } srml-support = { path = "../../srml/support" }
substrate-primitives = { path = "../../core/primitives" } substrate-primitives = { path = "../../core/primitives" }
substrate-client = { path = "../../core/client", optional = true } substrate-fg-primitives = { path = "../../core/finality-grandpa/primitives" }
substrate-client = { path = "../../core/client" }
substrate-keyring = { path = "../../core/keyring" } substrate-keyring = { path = "../../core/keyring" }
srml-balances = { path = "../../srml/balances" } srml-balances = { path = "../../srml/balances" }
srml-consensus = { path = "../../srml/consensus" } srml-consensus = { path = "../../srml/consensus" }
@@ -57,5 +58,6 @@ std = [
"serde_derive", "serde_derive",
"serde/std", "serde/std",
"safe-mix/std", "safe-mix/std",
"substrate-client", "substrate-client/std",
"substrate-fg-primitives/std",
] ]
+28 -1
View File
@@ -56,6 +56,7 @@ extern crate srml_upgrade_key as upgrade_key;
#[macro_use] #[macro_use]
extern crate sr_version as version; extern crate sr_version as version;
extern crate node_primitives; extern crate node_primitives;
extern crate substrate_fg_primitives;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use codec::{Encode, Decode}; use codec::{Encode, Decode};
@@ -72,7 +73,7 @@ use client::runtime_api::ApiExt;
use runtime_primitives::ApplyResult; use runtime_primitives::ApplyResult;
use runtime_primitives::transaction_validity::TransactionValidity; use runtime_primitives::transaction_validity::TransactionValidity;
use runtime_primitives::generic; use runtime_primitives::generic;
use runtime_primitives::traits::{Convert, BlakeTwo256, Block as BlockT}; use runtime_primitives::traits::{Convert, BlakeTwo256, Block as BlockT, DigestFor, NumberFor};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use runtime_primitives::traits::ApiRef; use runtime_primitives::traits::ApiRef;
#[cfg(feature = "std")] #[cfg(feature = "std")]
@@ -84,6 +85,7 @@ use council::seats as council_seats;
#[cfg(any(feature = "std", test))] #[cfg(any(feature = "std", test))]
use version::NativeVersion; use version::NativeVersion;
use substrate_primitives::OpaqueMetadata; use substrate_primitives::OpaqueMetadata;
use substrate_fg_primitives::{runtime::GrandpaApi, ScheduledChange};
#[cfg(any(feature = "std", test))] #[cfg(any(feature = "std", test))]
pub use runtime_primitives::BuildStorage; pub use runtime_primitives::BuildStorage;
@@ -395,6 +397,19 @@ impl client::runtime_api::Metadata<GBlock> for ClientWithApi {
} }
} }
#[cfg(feature = "std")]
impl substrate_fg_primitives::GrandpaApi<GBlock> for ClientWithApi {
fn grandpa_pending_change(&self, at: &GBlockId, digest: &DigestFor<GBlock>)
-> Result<Option<ScheduledChange<NumberFor<GBlock>>>, client::error::Error> {
self.call_api_at(at, "grandpa_pending_change", digest)
}
fn grandpa_authorities(&self, at: &GBlockId)
-> Result<Vec<(AuthorityId, u64)>, client::error::Error> {
self.call_api_at(at, "grandpa_authorities", &())
}
}
impl_runtime_apis! { impl_runtime_apis! {
impl Core<Block> for Runtime { impl Core<Block> for Runtime {
fn version() -> RuntimeVersion { fn version() -> RuntimeVersion {
@@ -447,4 +462,16 @@ impl_runtime_apis! {
Executive::validate_transaction(tx) Executive::validate_transaction(tx)
} }
} }
impl GrandpaApi<Block> for ClientWithApi {
fn grandpa_pending_change(digest: DigestFor<Block>)
-> Option<ScheduledChange<NumberFor<Block>>> {
unimplemented!("Robert, where is the impl?")
}
fn grandpa_authorities() -> Vec<(SessionKey, u64)> {
unimplemented!("Robert, where is the impl?")
}
}
} }
+13
View File
@@ -526,6 +526,7 @@ dependencies = [
"srml-treasury 0.1.0", "srml-treasury 0.1.0",
"srml-upgrade-key 0.1.0", "srml-upgrade-key 0.1.0",
"substrate-client 0.1.0", "substrate-client 0.1.0",
"substrate-fg-primitives 0.1.0",
"substrate-primitives 0.1.0", "substrate-primitives 0.1.0",
] ]
@@ -1272,6 +1273,18 @@ dependencies = [
"wasmi 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "wasmi 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "substrate-fg-primitives"
version = "0.1.0"
dependencies = [
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-primitives 0.1.0",
"sr-std 0.1.0",
"substrate-client 0.1.0",
"substrate-primitives 0.1.0",
]
[[package]] [[package]]
name = "substrate-keyring" name = "substrate-keyring"
version = "0.1.0" version = "0.1.0"
+2
View File
@@ -12,6 +12,7 @@ safe-mix = { version = "1.0", default-features = false }
parity-codec-derive = { version = "2.1" } parity-codec-derive = { version = "2.1" }
parity-codec = { version = "2.1", default-features = false } parity-codec = { version = "2.1", default-features = false }
substrate-primitives = { path = "../../../core/primitives", default-features = false } substrate-primitives = { path = "../../../core/primitives", default-features = false }
substrate-fg-primitives = { path = "../../../core/finality-grandpa/primitives", default-features = false }
substrate-client = { path = "../../../core/client", default-features = false } substrate-client = { path = "../../../core/client", default-features = false }
sr-std = { path = "../../../core/sr-std", default-features = false } sr-std = { path = "../../../core/sr-std", default-features = false }
srml-support = { path = "../../../srml/support", default-features = false } srml-support = { path = "../../../srml/support", default-features = false }
@@ -38,6 +39,7 @@ std = [
"parity-codec/std", "parity-codec/std",
"substrate-primitives/std", "substrate-primitives/std",
"substrate-client/std", "substrate-client/std",
"substrate-fg-primitives/std",
"sr-std/std", "sr-std/std",
"srml-support/std", "srml-support/std",
"srml-balances/std", "srml-balances/std",