From cbfc36b39f9ae42dabf3234ad0fea786e45d0211 Mon Sep 17 00:00:00 2001 From: Stanislav Tkach Date: Fri, 29 Mar 2019 18:41:22 +0200 Subject: [PATCH] Move authorities interface from Core to consensus (#1412) * Move authorities interface from Core to consensus f * notify all caches of block insert + create with up-to-date best_fin * merged authorities_are_cached from light_grandpa_import2 * Add ProvideCache trait * Create helper function for 'get_cache' * Fix some formatting * Bump impl version * Resolve wasm conflicts * Apply review comments * Use try_for_each * Move authorities interface from Core to consensus f * notify all caches of block insert + create with up-to-date best_fin * merged authorities_are_cached from light_grandpa_import2 * Add ProvideCache trait * Create helper function for 'get_cache' * Fix some formatting * Bump impl version * Resolve wasm conflicts * Apply review comments * Use try_for_each * Move authorities interface from Core to consensus f * notify all caches of block insert + create with up-to-date best_fin * merged authorities_are_cached from light_grandpa_import2 * Add ProvideCache trait * Create helper function for 'get_cache' * Fix some formatting * Bump impl version * Resolve wasm conflicts * Apply review comments * Use try_for_each * Increment impl_version * Update lib.rs --- substrate/Cargo.lock | 19 ++ substrate/README.adoc | 4 - substrate/core/client/db/Cargo.toml | 1 + substrate/core/client/db/src/cache/mod.rs | 170 +++++++---- substrate/core/client/db/src/lib.rs | 15 +- substrate/core/client/db/src/light.rs | 266 ++++++++++-------- substrate/core/client/src/backend.rs | 8 +- substrate/core/client/src/blockchain.rs | 16 +- substrate/core/client/src/client.rs | 74 ++--- substrate/core/client/src/error.rs | 6 - substrate/core/client/src/in_mem.rs | 75 ++--- substrate/core/client/src/light/backend.rs | 12 +- substrate/core/client/src/light/blockchain.rs | 22 +- .../core/client/src/light/call_executor.rs | 2 +- substrate/core/client/src/light/fetcher.rs | 7 +- substrate/core/client/src/runtime_api.rs | 5 +- substrate/core/consensus/aura/Cargo.toml | 1 + substrate/core/consensus/aura/src/lib.rs | 75 +++-- .../core/consensus/authorities/Cargo.toml | 29 ++ .../core/consensus/authorities/src/lib.rs | 31 ++ .../core/consensus/common/src/block_import.rs | 9 +- substrate/core/consensus/common/src/error.rs | 6 + .../core/consensus/common/src/import_queue.rs | 9 +- substrate/core/consensus/common/src/lib.rs | 10 +- substrate/core/finality-grandpa/src/import.rs | 8 +- substrate/core/finality-grandpa/src/tests.rs | 18 +- substrate/core/network/src/chain.rs | 13 +- substrate/core/rpc/src/state/tests.rs | 2 +- substrate/core/service/test/src/lib.rs | 3 +- .../core/sr-api-macros/tests/decl_and_impl.rs | 5 +- substrate/core/test-client/src/client_ext.rs | 5 +- substrate/core/test-runtime/Cargo.toml | 1 + substrate/core/test-runtime/src/lib.rs | 22 +- substrate/core/test-runtime/wasm/Cargo.lock | 15 + .../substrate_test_runtime.compact.wasm | Bin 60075 -> 60111 bytes substrate/node-template/runtime/Cargo.toml | 1 + substrate/node-template/runtime/src/lib.rs | 10 +- .../node-template/runtime/wasm/Cargo.lock | 15 + substrate/node-template/src/service.rs | 34 +-- substrate/node/runtime/Cargo.toml | 1 + substrate/node/runtime/src/lib.rs | 17 +- substrate/node/runtime/wasm/Cargo.lock | 15 + .../release/node_runtime.compact.wasm | Bin 968565 -> 968726 bytes substrate/srml/council/src/seats.rs | 2 +- 44 files changed, 650 insertions(+), 409 deletions(-) create mode 100644 substrate/core/consensus/authorities/Cargo.toml create mode 100644 substrate/core/consensus/authorities/src/lib.rs diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 6ff9929eb7..7edf2b97e2 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -1973,6 +1973,7 @@ dependencies = [ "srml-treasury 0.1.0", "substrate-client 0.1.0", "substrate-consensus-aura-primitives 0.1.0", + "substrate-consensus-authorities 0.1.0", "substrate-keyring 0.1.0", "substrate-offchain-primitives 0.1.0", "substrate-primitives 0.1.0", @@ -2030,6 +2031,7 @@ dependencies = [ "srml-timestamp 0.1.0", "substrate-client 0.1.0", "substrate-consensus-aura-primitives 0.1.0", + "substrate-consensus-authorities 0.1.0", "substrate-offchain-primitives 0.1.0", "substrate-primitives 0.1.0", ] @@ -3713,6 +3715,7 @@ dependencies = [ "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 0.1.0", "substrate-client 0.1.0", + "substrate-consensus-common 0.1.0", "substrate-executor 0.1.0", "substrate-keyring 0.1.0", "substrate-primitives 0.1.0", @@ -3741,6 +3744,7 @@ dependencies = [ "substrate-client 0.1.0", "substrate-consensus-aura-primitives 0.1.0", "substrate-consensus-aura-slots 0.1.0", + "substrate-consensus-authorities 0.1.0", "substrate-consensus-common 0.1.0", "substrate-executor 0.1.0", "substrate-inherents 0.1.0", @@ -3779,6 +3783,20 @@ dependencies = [ "tokio 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "substrate-consensus-authorities" +version = "0.1.0" +dependencies = [ + "parity-codec 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 0.1.0", + "sr-primitives 0.1.0", + "sr-std 0.1.0", + "sr-version 0.1.0", + "srml-support 0.1.0", + "substrate-client 0.1.0", + "substrate-primitives 0.1.0", +] + [[package]] name = "substrate-consensus-common" version = "0.1.0" @@ -4251,6 +4269,7 @@ dependencies = [ "srml-support 0.1.0", "substrate-client 0.1.0", "substrate-consensus-aura-primitives 0.1.0", + "substrate-consensus-authorities 0.1.0", "substrate-executor 0.1.0", "substrate-inherents 0.1.0", "substrate-keyring 0.1.0", diff --git a/substrate/README.adoc b/substrate/README.adoc index cf0e1970a7..92a5418170 100644 --- a/substrate/README.adoc +++ b/substrate/README.adoc @@ -63,10 +63,6 @@ impl_runtime_apis! { VERSION } - fn authorities() -> Vec { - Consensus::authorities() - } - fn execute_block(block: Block) { Executive::execute_block(block) } diff --git a/substrate/core/client/db/Cargo.toml b/substrate/core/client/db/Cargo.toml index 7fc1cc5d95..cc6e38d017 100644 --- a/substrate/core/client/db/Cargo.toml +++ b/substrate/core/client/db/Cargo.toml @@ -26,6 +26,7 @@ trie = { package = "substrate-trie", path = "../../trie" } kvdb-memorydb = { git = "https://github.com/paritytech/parity-common", rev="b0317f649ab2c665b7987b8475878fc4d2e1f81d" } substrate-keyring = { path = "../../keyring" } test-client = { package = "substrate-test-client", path = "../../test-client" } +consensus_common = { package = "substrate-consensus-common", path = "../../consensus/common" } env_logger = { version = "0.6" } [features] diff --git a/substrate/core/client/db/src/cache/mod.rs b/substrate/core/client/db/src/cache/mod.rs index 3d669e392d..93555f3c15 100644 --- a/substrate/core/client/db/src/cache/mod.rs +++ b/substrate/core/client/db/src/cache/mod.rs @@ -16,7 +16,7 @@ //! DB-backed cache of blockchain data. -use std::sync::Arc; +use std::{sync::Arc, collections::HashMap}; use parking_lot::RwLock; use kvdb::{KeyValueDB, DBTransaction}; @@ -25,7 +25,7 @@ use client::blockchain::Cache as BlockchainCache; use client::error::Result as ClientResult; use parity_codec::{Encode, Decode}; use runtime_primitives::generic::BlockId; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, As, AuthorityIdFor}; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, As}; use crate::utils::{self, COLUMN_META}; use self::list_cache::ListCache; @@ -64,7 +64,12 @@ impl CacheItemT for T where T: Clone + Decode + Encode + PartialEq {} /// Database-backed blockchain data cache. pub struct DbCache { - authorities_at: ListCache>, self::list_storage::DbStorage>, + cache_at: HashMap, ListCache, self::list_storage::DbStorage>>, + db: Arc, + key_lookup_column: Option, + header_column: Option, + authorities_column: Option, + best_finalized_block: ComplexBlockId, } impl DbCache { @@ -76,19 +81,13 @@ impl DbCache { authorities_column: Option, best_finalized_block: ComplexBlockId, ) -> Self { - DbCache { - authorities_at: ListCache::new( - self::list_storage::DbStorage::new(b"auth".to_vec(), db, - self::list_storage::DbColumns { - meta: COLUMN_META, - key_lookup: key_lookup_column, - header: header_column, - cache: authorities_column, - }, - ), - As::sa(PRUNE_DEPTH), - best_finalized_block, - ), + Self { + cache_at: HashMap::new(), + db, + key_lookup_column, + header_column, + authorities_column, + best_finalized_block, } } @@ -97,35 +96,82 @@ impl DbCache { DbCacheTransaction { cache: self, tx, - authorities_at_op: None, + cache_at_op: HashMap::new(), + best_finalized_block: None, } } /// Run post-commit cache operations. pub fn commit(&mut self, ops: DbCacheTransactionOps) { - if let Some(authorities_at_op) = ops.authorities_at_op { - self.authorities_at.on_transaction_commit(authorities_at_op); + for (name, op) in ops.cache_at_op.into_iter() { + self.get_cache(name).on_transaction_commit(op); + } + if let Some(best_finalized_block) = ops.best_finalized_block { + self.best_finalized_block = best_finalized_block; } } + + /// Creates `ListCache` with the given name or returns a reference to the existing. + fn get_cache(&mut self, name: Vec) -> &mut ListCache, self::list_storage::DbStorage> { + get_cache_helper( + &mut self.cache_at, + name, + &self.db, + self.key_lookup_column, + self.header_column, + self.authorities_column, + &self.best_finalized_block + ) + } +} + +// This helper is needed because otherwise the borrow checker will require to +// clone all parameters outside of the closure. +fn get_cache_helper<'a, Block: BlockT>( + cache_at: &'a mut HashMap, ListCache, self::list_storage::DbStorage>>, + name: Vec, + db: &Arc, + key_lookup: Option, + header: Option, + cache: Option, + best_finalized_block: &ComplexBlockId, +) -> &'a mut ListCache, self::list_storage::DbStorage> { + cache_at.entry(name.clone()).or_insert_with(|| { + ListCache::new( + self::list_storage::DbStorage::new(name, db.clone(), + self::list_storage::DbColumns { + meta: COLUMN_META, + key_lookup, + header, + cache, + }, + ), + As::sa(PRUNE_DEPTH), + best_finalized_block.clone(), + ) + }) } /// Cache operations that are to be committed after database transaction is committed. pub struct DbCacheTransactionOps { - authorities_at_op: Option>>>, + cache_at_op: HashMap, self::list_cache::CommitOperation>>, + best_finalized_block: Option>, } /// Database-backed blockchain data cache transaction valid for single block import. pub struct DbCacheTransaction<'a, Block: BlockT> { cache: &'a mut DbCache, tx: &'a mut DBTransaction, - authorities_at_op: Option>>>, + cache_at_op: HashMap, self::list_cache::CommitOperation>>, + best_finalized_block: Option>, } impl<'a, Block: BlockT> DbCacheTransaction<'a, Block> { /// Convert transaction into post-commit operations set. pub fn into_ops(self) -> DbCacheTransactionOps { DbCacheTransactionOps { - authorities_at_op: self.authorities_at_op, + cache_at_op: self.cache_at_op, + best_finalized_block: self.best_finalized_block, } } @@ -134,21 +180,42 @@ impl<'a, Block: BlockT> DbCacheTransaction<'a, Block> { mut self, parent: ComplexBlockId, block: ComplexBlockId, - authorities_at: Option>>, + data_at: HashMap, Vec>, is_final: bool, ) -> ClientResult { - assert!(self.authorities_at_op.is_none()); + assert!(self.cache_at_op.is_empty()); - self.authorities_at_op = self.cache.authorities_at.on_block_insert( - &mut self::list_storage::DbStorageTransaction::new( - self.cache.authorities_at.storage(), - &mut self.tx - ), - parent, - block, - authorities_at, - is_final, - )?; + // prepare list of caches that are not update + // (we might still need to do some cache maintenance in this case) + let missed_caches = self.cache.cache_at.keys() + .filter(|cache| !data_at.contains_key(cache.clone())) + .cloned() + .collect::>(); + + let mut insert_op = |name: Vec, value: Option>| -> Result<(), client::error::Error> { + let cache = self.cache.get_cache(name.clone()); + let op = cache.on_block_insert( + &mut self::list_storage::DbStorageTransaction::new( + cache.storage(), + &mut self.tx, + ), + parent.clone(), + block.clone(), + value.or(cache.value_at_block(&parent)?), + is_final, + )?; + if let Some(op) = op { + self.cache_at_op.insert(name, op); + } + Ok(()) + }; + + data_at.into_iter().try_for_each(|(name, data)| insert_op(name, Some(data)))?; + missed_caches.into_iter().try_for_each(|name| insert_op(name, None))?; + + if is_final { + self.best_finalized_block = Some(block); + } Ok(self) } @@ -159,16 +226,24 @@ impl<'a, Block: BlockT> DbCacheTransaction<'a, Block> { parent: ComplexBlockId, block: ComplexBlockId ) -> ClientResult { - assert!(self.authorities_at_op.is_none()); + assert!(self.cache_at_op.is_empty()); - self.authorities_at_op = self.cache.authorities_at.on_block_finalize( - &mut self::list_storage::DbStorageTransaction::new( - self.cache.authorities_at.storage(), - &mut self.tx - ), - parent, - block, - )?; + for (name, cache_at) in self.cache.cache_at.iter() { + let op = cache_at.on_block_finalize( + &mut self::list_storage::DbStorageTransaction::new( + cache_at.storage(), + &mut self.tx + ), + parent.clone(), + block.clone(), + )?; + + if let Some(op) = op { + self.cache_at_op.insert(name.to_owned(), op); + } + } + + self.best_finalized_block = Some(block); Ok(self) } @@ -178,12 +253,12 @@ impl<'a, Block: BlockT> DbCacheTransaction<'a, Block> { pub struct DbCacheSync(pub RwLock>); impl BlockchainCache for DbCacheSync { - fn authorities_at(&self, at: BlockId) -> Option>> { + fn get_at(&self, key: &[u8], at: &BlockId) -> Option> { let cache = self.0.read(); - let storage = cache.authorities_at.storage(); + let storage = cache.cache_at.get(key)?.storage(); let db = storage.db(); let columns = storage.columns(); - let at = match at { + let at = match *at { BlockId::Hash(hash) => { let header = utils::read_header::( &**db, @@ -202,6 +277,7 @@ impl BlockchainCache for DbCacheSync { }, }; - cache.authorities_at.value_at_block(&at).ok()? + cache.cache_at.get(key)?.value_at_block(&at).ok()? } } + diff --git a/substrate/core/client/db/src/lib.rs b/substrate/core/client/db/src/lib.rs index 93adb99024..dde01bf929 100644 --- a/substrate/core/client/db/src/lib.rs +++ b/substrate/core/client/db/src/lib.rs @@ -33,6 +33,7 @@ mod utils; use std::sync::Arc; use std::path::PathBuf; use std::io; +use std::collections::HashMap; use client::backend::NewBlockState; use client::blockchain::HeaderBackend; @@ -45,7 +46,7 @@ use parking_lot::RwLock; use primitives::{H256, Blake2Hasher, ChangesTrieConfiguration, convert_hash}; use primitives::storage::well_known_keys; use runtime_primitives::{generic::BlockId, Justification, StorageOverlay, ChildrenStorageOverlay}; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, NumberFor, Zero, Digest, DigestItem, AuthorityIdFor}; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, NumberFor, Zero, Digest, DigestItem}; use runtime_primitives::BuildStorage; use state_machine::backend::Backend as StateBackend; use executor::RuntimeInfo; @@ -243,7 +244,7 @@ impl client::blockchain::Backend for BlockchainDb { Ok(self.meta.read().finalized_hash.clone()) } - fn cache(&self) -> Option<&client::blockchain::Cache> { + fn cache(&self) -> Option>> { None } @@ -256,6 +257,12 @@ impl client::blockchain::Backend for BlockchainDb { } } +impl client::blockchain::ProvideCache for BlockchainDb { + fn cache(&self) -> Option>> { + None + } +} + /// Database transaction pub struct BlockImportOperation { old_state: CachingState, @@ -306,8 +313,8 @@ where Block: BlockT, Ok(()) } - fn update_authorities(&mut self, _authorities: Vec>) { - // currently authorities are not cached on full nodes + fn update_cache(&mut self, _cache: HashMap, Vec>) { + // Currently cache isn't implemented on full nodes. } fn update_db_storage(&mut self, update: PrefixedMemoryDB) -> Result<(), client::error::Error> { diff --git a/substrate/core/client/db/src/light.rs b/substrate/core/client/db/src/light.rs index d99ef503b2..f064035d51 100644 --- a/substrate/core/client/db/src/light.rs +++ b/substrate/core/client/db/src/light.rs @@ -16,7 +16,7 @@ //! RocksDB-based light client blockchain storage. -use std::sync::Arc; +use std::{sync::Arc, collections::HashMap}; use parking_lot::RwLock; use kvdb::{KeyValueDB, DBTransaction}; @@ -32,7 +32,7 @@ use parity_codec::{Decode, Encode}; use primitives::Blake2Hasher; use runtime_primitives::generic::BlockId; use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, - Zero, One, As, NumberFor, Digest, DigestItem, AuthorityIdFor}; + Zero, One, As, NumberFor, Digest, DigestItem}; use crate::cache::{DbCacheSync, DbCache, ComplexBlockId}; use crate::utils::{self, meta_keys, Meta, db_err, open_database, read_db, block_id_to_lookup_key, read_meta}; @@ -59,7 +59,7 @@ pub struct LightStorage { db: Arc, meta: RwLock, Block::Hash>>, leaves: RwLock>>, - cache: DbCacheSync, + cache: Arc>, } impl LightStorage @@ -96,7 +96,7 @@ impl LightStorage Ok(LightStorage { db, meta: RwLock::new(meta), - cache: DbCacheSync(RwLock::new(cache)), + cache: Arc::new(DbCacheSync(RwLock::new(cache))), leaves: RwLock::new(leaves), }) } @@ -370,7 +370,7 @@ impl LightBlockchainStorage for LightStorage fn import_header( &self, header: Block::Header, - authorities: Option>>, + cache_at: HashMap, Vec>, leaf_state: NewBlockState, aux_ops: Vec<(Vec, Option>)>, ) -> ClientResult<()> { @@ -432,7 +432,7 @@ impl LightBlockchainStorage for LightStorage .on_block_insert( ComplexBlockId::new(*header.parent_hash(), if number.is_zero() { Zero::zero() } else { number - One::one() }), ComplexBlockId::new(hash, number), - authorities, + cache_at, finalized, )? .into_ops(); @@ -521,8 +521,8 @@ impl LightBlockchainStorage for LightStorage Ok(self.meta.read().finalized_hash.clone()) } - fn cache(&self) -> Option<&BlockchainCache> { - Some(&self.cache) + fn cache(&self) -> Option>> { + Some(self.cache.clone()) } } @@ -538,6 +538,8 @@ pub(crate) mod tests { use client::cht; use runtime_primitives::generic::DigestItem; use runtime_primitives::testing::{H256 as Hash, Header, Block as RawBlock, ExtrinsicWrapper}; + use runtime_primitives::traits::AuthorityIdFor; + use consensus_common::well_known_cache_keys; use super::*; type Block = RawBlock>; @@ -567,41 +569,41 @@ pub(crate) mod tests { pub fn insert_block Header>( db: &LightStorage, - authorities: Option>>, + cache: HashMap, Vec>, header: F, ) -> Hash { let header = header(); let hash = header.hash(); - db.import_header(header, authorities, NewBlockState::Best, Vec::new()).unwrap(); + db.import_header(header, cache, NewBlockState::Best, Vec::new()).unwrap(); hash } fn insert_final_block Header>( db: &LightStorage, - authorities: Option>>, + cache: HashMap, Vec>, header: F, ) -> Hash { let header = header(); let hash = header.hash(); - db.import_header(header, authorities, NewBlockState::Final, Vec::new()).unwrap(); + db.import_header(header, cache, NewBlockState::Final, Vec::new()).unwrap(); hash } fn insert_non_best_block Header>( db: &LightStorage, - authorities: Option>>, + cache: HashMap, Vec>, header: F, ) -> Hash { let header = header(); let hash = header.hash(); - db.import_header(header, authorities, NewBlockState::Normal, Vec::new()).unwrap(); + db.import_header(header, cache, NewBlockState::Normal, Vec::new()).unwrap(); hash } #[test] fn returns_known_header() { let db = LightStorage::new_test(); - let known_hash = insert_block(&db, None, || default_header(&Default::default(), 0)); + let known_hash = insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0)); let header_by_hash = db.header(BlockId::Hash(known_hash)).unwrap().unwrap(); let header_by_number = db.header(BlockId::Number(0)).unwrap().unwrap(); assert_eq!(header_by_hash, header_by_number); @@ -617,12 +619,12 @@ pub(crate) mod tests { #[test] fn returns_info() { let db = LightStorage::new_test(); - let genesis_hash = insert_block(&db, None, || default_header(&Default::default(), 0)); + let genesis_hash = insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0)); let info = db.info().unwrap(); assert_eq!(info.best_hash, genesis_hash); assert_eq!(info.best_number, 0); assert_eq!(info.genesis_hash, genesis_hash); - let best_hash = insert_block(&db, None, || default_header(&genesis_hash, 1)); + let best_hash = insert_block(&db, HashMap::new(), || default_header(&genesis_hash, 1)); let info = db.info().unwrap(); assert_eq!(info.best_hash, best_hash); assert_eq!(info.best_number, 1); @@ -632,7 +634,7 @@ pub(crate) mod tests { #[test] fn returns_block_status() { let db = LightStorage::new_test(); - let genesis_hash = insert_block(&db, None, || default_header(&Default::default(), 0)); + let genesis_hash = insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0)); assert_eq!(db.status(BlockId::Hash(genesis_hash)).unwrap(), BlockStatus::InChain); assert_eq!(db.status(BlockId::Number(0)).unwrap(), BlockStatus::InChain); assert_eq!(db.status(BlockId::Hash(Hash::from_low_u64_be(1))).unwrap(), BlockStatus::Unknown); @@ -642,7 +644,7 @@ pub(crate) mod tests { #[test] fn returns_block_hash() { let db = LightStorage::new_test(); - let genesis_hash = insert_block(&db, None, || default_header(&Default::default(), 0)); + let genesis_hash = insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0)); assert_eq!(db.hash(0).unwrap(), Some(genesis_hash)); assert_eq!(db.hash(1).unwrap(), None); } @@ -651,11 +653,11 @@ pub(crate) mod tests { fn import_header_works() { let db = LightStorage::new_test(); - let genesis_hash = insert_block(&db, None, || default_header(&Default::default(), 0)); + let genesis_hash = insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0)); assert_eq!(db.db.iter(columns::HEADER).count(), 1); assert_eq!(db.db.iter(columns::KEY_LOOKUP).count(), 2); - let _ = insert_block(&db, None, || default_header(&genesis_hash, 1)); + let _ = insert_block(&db, HashMap::new(), || default_header(&genesis_hash, 1)); assert_eq!(db.db.iter(columns::HEADER).count(), 2); assert_eq!(db.db.iter(columns::KEY_LOOKUP).count(), 4); } @@ -666,25 +668,25 @@ pub(crate) mod tests { let db = LightStorage::new_test(); // insert genesis block header (never pruned) - let mut prev_hash = insert_final_block(&db, None, || header_producer(&Default::default(), 0)); + let mut prev_hash = insert_final_block(&db, HashMap::new(), || header_producer(&Default::default(), 0)); // insert SIZE blocks && ensure that nothing is pruned for number in 0..cht::SIZE { - prev_hash = insert_block(&db, None, || header_producer(&prev_hash, 1 + number)); + prev_hash = insert_block(&db, HashMap::new(), || header_producer(&prev_hash, 1 + number)); } assert_eq!(db.db.iter(columns::HEADER).count(), (1 + cht::SIZE) as usize); assert_eq!(db.db.iter(columns::CHT).count(), 0); // insert next SIZE blocks && ensure that nothing is pruned for number in 0..cht::SIZE { - prev_hash = insert_block(&db, None, || header_producer(&prev_hash, 1 + cht::SIZE + number)); + prev_hash = insert_block(&db, HashMap::new(), || header_producer(&prev_hash, 1 + cht::SIZE + number)); } assert_eq!(db.db.iter(columns::HEADER).count(), (1 + cht::SIZE + cht::SIZE) as usize); assert_eq!(db.db.iter(columns::CHT).count(), 0); // insert block #{2 * cht::SIZE + 1} && check that new CHT is created + headers of this CHT are pruned // nothing is yet finalized, so nothing is pruned. - prev_hash = insert_block(&db, None, || header_producer(&prev_hash, 1 + cht::SIZE + cht::SIZE)); + prev_hash = insert_block(&db, HashMap::new(), || header_producer(&prev_hash, 1 + cht::SIZE + cht::SIZE)); assert_eq!(db.db.iter(columns::HEADER).count(), (2 + cht::SIZE + cht::SIZE) as usize); assert_eq!(db.db.iter(columns::CHT).count(), 0); @@ -733,9 +735,9 @@ pub(crate) mod tests { let db = LightStorage::new_test(); // insert 1 + SIZE + SIZE + 1 blocks so that CHT#0 is created - let mut prev_hash = insert_final_block(&db, None, || header_with_changes_trie(&Default::default(), 0)); + let mut prev_hash = insert_final_block(&db, HashMap::new(), || header_with_changes_trie(&Default::default(), 0)); for i in 1..1 + cht::SIZE + cht::SIZE + 1 { - prev_hash = insert_block(&db, None, || header_with_changes_trie(&prev_hash, i as u64)); + prev_hash = insert_block(&db, HashMap::new(), || header_with_changes_trie(&prev_hash, i as u64)); db.finalize_header(BlockId::Hash(prev_hash)).unwrap(); } @@ -755,16 +757,16 @@ pub(crate) mod tests { #[test] fn tree_route_works() { let db = LightStorage::new_test(); - let block0 = insert_block(&db, None, || default_header(&Default::default(), 0)); + let block0 = insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0)); // fork from genesis: 3 prong. - let a1 = insert_block(&db, None, || default_header(&block0, 1)); - let a2 = insert_block(&db, None, || default_header(&a1, 2)); - let a3 = insert_block(&db, None, || default_header(&a2, 3)); + let a1 = insert_block(&db, HashMap::new(), || default_header(&block0, 1)); + let a2 = insert_block(&db, HashMap::new(), || default_header(&a1, 2)); + let a3 = insert_block(&db, HashMap::new(), || default_header(&a2, 3)); // fork from genesis: 2 prong. - let b1 = insert_block(&db, None, || header_with_extrinsics_root(&block0, 1, Hash::from([1; 32]))); - let b2 = insert_block(&db, None, || default_header(&b1, 2)); + let b1 = insert_block(&db, HashMap::new(), || header_with_extrinsics_root(&block0, 1, Hash::from([1; 32]))); + let b2 = insert_block(&db, HashMap::new(), || default_header(&b1, 2)); { let tree_route = ::client::blockchain::tree_route( @@ -816,44 +818,64 @@ pub(crate) mod tests { } #[test] - fn authorites_are_cached() { + fn authorities_are_cached() { let db = LightStorage::new_test(); fn run_checks(db: &LightStorage, max: u64, checks: &[(u64, Option>>)]) { for (at, expected) in checks.iter().take_while(|(at, _)| *at <= max) { - let actual = db.cache().authorities_at(BlockId::Number(*at)); + let actual = get_authorities(db.cache(), BlockId::Number(*at)); assert_eq!(*expected, actual); } } + fn same_authorities() -> HashMap, Vec> { + HashMap::new() + } + + fn make_authorities(authorities: Vec) -> HashMap, Vec> { + let mut map = HashMap::new(); + map.insert(well_known_cache_keys::AUTHORITIES.to_vec(), authorities.encode()); + map + } + + fn get_authorities(cache: &BlockchainCache, at: BlockId) -> Option> { + cache.get_at(well_known_cache_keys::AUTHORITIES, &at).and_then(|val| Decode::decode(&mut &val[..])) + } + + let auth1 = || AuthorityId::from_raw([1u8; 32]); + let auth2 = || AuthorityId::from_raw([2u8; 32]); + let auth3 = || AuthorityId::from_raw([3u8; 32]); + let auth4 = || AuthorityId::from_raw([4u8; 32]); + let auth5 = || AuthorityId::from_raw([5u8; 32]); + let auth6 = || AuthorityId::from_raw([6u8; 32]); + let (hash2, hash6) = { // first few blocks are instantly finalized - // B0(None) -> B1(None) -> B2(1) -> B3(1) -> B4(1, 2) -> B5(1, 2) -> B6(None) + // B0(None) -> B1(None) -> B2(1) -> B3(1) -> B4(1, 2) -> B5(1, 2) -> B6(1, 2) let checks = vec![ (0, None), (1, None), - (2, Some(vec![AuthorityId::from_raw([1u8; 32])])), - (3, Some(vec![AuthorityId::from_raw([1u8; 32])])), - (4, Some(vec![AuthorityId::from_raw([1u8; 32]), AuthorityId::from_raw([2u8; 32])])), - (5, Some(vec![AuthorityId::from_raw([1u8; 32]), AuthorityId::from_raw([2u8; 32])])), - (6, None), - (7, None), // block will work for 'future' block too + (2, Some(vec![auth1()])), + (3, Some(vec![auth1()])), + (4, Some(vec![auth1(), auth2()])), + (5, Some(vec![auth1(), auth2()])), + (6, Some(vec![auth1(), auth2()])), ]; - let hash0 = insert_final_block(&db, None, || default_header(&Default::default(), 0)); + let hash0 = insert_final_block(&db, same_authorities(), || default_header(&Default::default(), 0)); run_checks(&db, 0, &checks); - let hash1 = insert_final_block(&db, None, || default_header(&hash0, 1)); + let hash1 = insert_final_block(&db, same_authorities(), || default_header(&hash0, 1)); run_checks(&db, 1, &checks); - let hash2 = insert_final_block(&db, Some(vec![AuthorityId::from_raw([1u8; 32])]), || default_header(&hash1, 2)); + let hash2 = insert_final_block(&db, make_authorities(vec![auth1()]), || default_header(&hash1, 2)); run_checks(&db, 2, &checks); - let hash3 = insert_final_block(&db, Some(vec![AuthorityId::from_raw([1u8; 32])]), || default_header(&hash2, 3)); + let hash3 = insert_final_block(&db, make_authorities(vec![auth1()]), || default_header(&hash2, 3)); run_checks(&db, 3, &checks); - let hash4 = insert_final_block(&db, Some(vec![AuthorityId::from_raw([1u8; 32]), AuthorityId::from_raw([2u8; 32])]), || default_header(&hash3, 4)); + let hash4 = insert_final_block(&db, make_authorities(vec![auth1(), auth2()]), || default_header(&hash3, 4)); run_checks(&db, 4, &checks); - let hash5 = insert_final_block(&db, Some(vec![AuthorityId::from_raw([1u8; 32]), AuthorityId::from_raw([2u8; 32])]), || default_header(&hash4, 5)); + let hash5 = insert_final_block(&db, make_authorities(vec![auth1(), auth2()]), || default_header(&hash4, 5)); run_checks(&db, 5, &checks); - let hash6 = insert_final_block(&db, None, || default_header(&hash5, 6)); - run_checks(&db, 7, &checks); + let hash6 = insert_final_block(&db, same_authorities(), || default_header(&hash5, 6)); + run_checks(&db, 6, &checks); (hash2, hash6) }; @@ -862,10 +884,10 @@ pub(crate) mod tests { // some older non-best blocks are inserted // ... -> B2(1) -> B2_1(1) -> B2_2(2) // => the cache ignores all writes before best finalized block - let hash2_1 = insert_non_best_block(&db, Some(vec![AuthorityId::from_raw([1u8; 32])]), || default_header(&hash2, 3)); - assert_eq!(None, db.cache().authorities_at(BlockId::Hash(hash2_1))); - let hash2_2 = insert_non_best_block(&db, Some(vec![AuthorityId::from_raw([1u8; 32]), AuthorityId::from_raw([2u8; 32])]), || default_header(&hash2_1, 4)); - assert_eq!(None, db.cache().authorities_at(BlockId::Hash(hash2_2))); + let hash2_1 = insert_non_best_block(&db, make_authorities(vec![auth1()]), || default_header(&hash2, 3)); + assert_eq!(None, get_authorities(db.cache(), BlockId::Hash(hash2_1))); + let hash2_2 = insert_non_best_block(&db, make_authorities(vec![auth1(), auth2()]), || default_header(&hash2_1, 4)); + assert_eq!(None, get_authorities(db.cache(), BlockId::Hash(hash2_2))); } let (hash7, hash8, hash6_1, hash6_2, hash6_1_1, hash6_1_2) = { @@ -875,39 +897,57 @@ pub(crate) mod tests { // \> B6_1_1(5) // \> B6_1_2(6) -> B6_1_3(7) - let hash7 = insert_block(&db, Some(vec![AuthorityId::from_raw([3u8; 32])]), || default_header(&hash6, 7)); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6)), None); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash7)), Some(vec![AuthorityId::from_raw([3u8; 32])])); - let hash8 = insert_block(&db, Some(vec![AuthorityId::from_raw([3u8; 32])]), || default_header(&hash7, 8)); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6)), None); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash7)), Some(vec![AuthorityId::from_raw([3u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash8)), Some(vec![AuthorityId::from_raw([3u8; 32])])); - let hash6_1 = insert_block(&db, Some(vec![AuthorityId::from_raw([4u8; 32])]), || default_header(&hash6, 7)); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6)), None); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash7)), Some(vec![AuthorityId::from_raw([3u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash8)), Some(vec![AuthorityId::from_raw([3u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1)), Some(vec![AuthorityId::from_raw([4u8; 32])])); - let hash6_1_1 = insert_non_best_block(&db, Some(vec![AuthorityId::from_raw([5u8; 32])]), || default_header(&hash6_1, 8)); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6)), None); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash7)), Some(vec![AuthorityId::from_raw([3u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash8)), Some(vec![AuthorityId::from_raw([3u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1)), Some(vec![AuthorityId::from_raw([4u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1_1)), Some(vec![AuthorityId::from_raw([5u8; 32])])); - let hash6_1_2 = insert_non_best_block(&db, Some(vec![AuthorityId::from_raw([6u8; 32])]), || default_header(&hash6_1, 8)); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6)), None); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash7)), Some(vec![AuthorityId::from_raw([3u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash8)), Some(vec![AuthorityId::from_raw([3u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1)), Some(vec![AuthorityId::from_raw([4u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1_1)), Some(vec![AuthorityId::from_raw([5u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1_2)), Some(vec![AuthorityId::from_raw([6u8; 32])])); - let hash6_2 = insert_block(&db, Some(vec![AuthorityId::from_raw([4u8; 32])]), || default_header(&hash6_1, 8)); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6)), None); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash7)), Some(vec![AuthorityId::from_raw([3u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash8)), Some(vec![AuthorityId::from_raw([3u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1)), Some(vec![AuthorityId::from_raw([4u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1_1)), Some(vec![AuthorityId::from_raw([5u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1_2)), Some(vec![AuthorityId::from_raw([6u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_2)), Some(vec![AuthorityId::from_raw([4u8; 32])])); + let hash7 = insert_block(&db, make_authorities(vec![auth3()]), || default_header(&hash6, 7)); + assert_eq!( + get_authorities(db.cache(), BlockId::Hash(hash6)), + Some(vec![auth1(), auth2()]), + ); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash7)), Some(vec![auth3()])); + let hash8 = insert_block(&db, make_authorities(vec![auth3()]), || default_header(&hash7, 8)); + assert_eq!( + get_authorities(db.cache(), BlockId::Hash(hash6)), + Some(vec![auth1(), auth2()]), + ); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash7)), Some(vec![auth3()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash8)), Some(vec![auth3()])); + let hash6_1 = insert_block(&db, make_authorities(vec![auth4()]), || default_header(&hash6, 7)); + assert_eq!( + get_authorities(db.cache(), BlockId::Hash(hash6)), + Some(vec![auth1(), auth2()]), + ); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash7)), Some(vec![auth3()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash8)), Some(vec![auth3()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1)), Some(vec![auth4()])); + let hash6_1_1 = insert_non_best_block(&db, make_authorities(vec![auth5()]), || default_header(&hash6_1, 8)); + assert_eq!( + get_authorities(db.cache(), BlockId::Hash(hash6)), + Some(vec![auth1(), auth2()]), + ); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash7)), Some(vec![auth3()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash8)), Some(vec![auth3()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1)), Some(vec![auth4()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1_1)), Some(vec![auth5()])); + let hash6_1_2 = insert_non_best_block(&db, make_authorities(vec![auth6()]), || default_header(&hash6_1, 8)); + assert_eq!( + get_authorities(db.cache(), BlockId::Hash(hash6)), + Some(vec![auth1(), auth2()]), + ); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash7)), Some(vec![auth3()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash8)), Some(vec![auth3()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1)), Some(vec![auth4()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1_1)), Some(vec![auth5()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1_2)), Some(vec![auth6()])); + let hash6_2 = insert_block(&db, make_authorities(vec![auth4()]), || default_header(&hash6_1, 8)); + assert_eq!( + get_authorities(db.cache(), BlockId::Hash(hash6)), + Some(vec![auth1(), auth2()]), + ); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash7)), Some(vec![auth3()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash8)), Some(vec![auth3()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1)), Some(vec![auth4()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1_1)), Some(vec![auth5()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1_2)), Some(vec![auth6()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_2)), Some(vec![auth4()])); (hash7, hash8, hash6_1, hash6_2, hash6_1_1, hash6_1_2) }; @@ -915,29 +955,35 @@ pub(crate) mod tests { { // finalize block hash6_1 db.finalize_header(BlockId::Hash(hash6_1)).unwrap(); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6)), None); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash7)), None); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash8)), None); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1)), Some(vec![AuthorityId::from_raw([4u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1_1)), Some(vec![AuthorityId::from_raw([5u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1_2)), Some(vec![AuthorityId::from_raw([6u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_2)), Some(vec![AuthorityId::from_raw([4u8; 32])])); + assert_eq!( + get_authorities(db.cache(), BlockId::Hash(hash6)), + Some(vec![auth1(), auth2()]), + ); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash7)), None); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash8)), None); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1)), Some(vec![auth4()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1_1)), Some(vec![auth5()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1_2)), Some(vec![auth6()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_2)), Some(vec![auth4()])); // finalize block hash6_2 db.finalize_header(BlockId::Hash(hash6_2)).unwrap(); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6)), None); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash7)), None); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash8)), None); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1)), Some(vec![AuthorityId::from_raw([4u8; 32])])); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1_1)), None); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_1_2)), None); - assert_eq!(db.cache().authorities_at(BlockId::Hash(hash6_2)), Some(vec![AuthorityId::from_raw([4u8; 32])])); + assert_eq!( + get_authorities(db.cache(), BlockId::Hash(hash6)), + Some(vec![auth1(), auth2()]), + ); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash7)), None); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash8)), None); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1)), Some(vec![auth4()])); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1_1)), None); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_1_2)), None); + assert_eq!(get_authorities(db.cache(), BlockId::Hash(hash6_2)), Some(vec![auth4()])); } } #[test] fn database_is_reopened() { let db = LightStorage::new_test(); - let hash0 = insert_final_block(&db, None, || default_header(&Default::default(), 0)); + let hash0 = insert_final_block(&db, HashMap::new(), || default_header(&Default::default(), 0)); assert_eq!(db.info().unwrap().best_hash, hash0); assert_eq!(db.header(BlockId::Hash(hash0)).unwrap().unwrap().hash(), hash0); @@ -960,7 +1006,7 @@ pub(crate) mod tests { assert_eq!(db.get_aux(&[3]).unwrap(), None); // delete aux1 + insert aux3 using import operation - db.import_header(default_header(&Default::default(), 0), None, NewBlockState::Best, vec![ + db.import_header(default_header(&Default::default(), 0), HashMap::new(), NewBlockState::Best, vec![ (vec![3], Some(vec![103])), (vec![1], None), ]).unwrap(); @@ -974,17 +1020,17 @@ pub(crate) mod tests { #[test] fn test_leaves_pruned_on_finality() { let db = LightStorage::::new_test(); - let block0 = insert_block(&db, None, || default_header(&Default::default(), 0)); + let block0 = insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0)); - let block1_a = insert_block(&db, None, || default_header(&block0, 1)); - let block1_b = insert_block(&db, None, || header_with_extrinsics_root(&block0, 1, [1; 32].into())); - let block1_c = insert_block(&db, None, || header_with_extrinsics_root(&block0, 1, [2; 32].into())); + let block1_a = insert_block(&db, HashMap::new(), || default_header(&block0, 1)); + let block1_b = insert_block(&db, HashMap::new(), || header_with_extrinsics_root(&block0, 1, [1; 32].into())); + let block1_c = insert_block(&db, HashMap::new(), || header_with_extrinsics_root(&block0, 1, [2; 32].into())); assert_eq!(db.leaves.read().hashes(), vec![block1_a, block1_b, block1_c]); - let block2_a = insert_block(&db, None, || default_header(&block1_a, 2)); - let block2_b = insert_block(&db, None, || header_with_extrinsics_root(&block1_b, 2, [1; 32].into())); - let block2_c = insert_block(&db, None, || header_with_extrinsics_root(&block1_b, 2, [2; 32].into())); + let block2_a = insert_block(&db, HashMap::new(), || default_header(&block1_a, 2)); + let block2_b = insert_block(&db, HashMap::new(), || header_with_extrinsics_root(&block1_b, 2, [1; 32].into())); + let block2_c = insert_block(&db, HashMap::new(), || header_with_extrinsics_root(&block1_b, 2, [2; 32].into())); assert_eq!(db.leaves.read().hashes(), vec![block2_a, block2_b, block2_c, block1_c]); diff --git a/substrate/core/client/src/backend.rs b/substrate/core/client/src/backend.rs index 9b063177ff..864d04ed61 100644 --- a/substrate/core/client/src/backend.rs +++ b/substrate/core/client/src/backend.rs @@ -16,10 +16,11 @@ //! Substrate Client data backend +use std::collections::HashMap; use crate::error; use primitives::ChangesTrieConfiguration; use runtime_primitives::{generic::BlockId, Justification, StorageOverlay, ChildrenStorageOverlay}; -use runtime_primitives::traits::{AuthorityIdFor, Block as BlockT, NumberFor}; +use runtime_primitives::traits::{Block as BlockT, NumberFor}; use state_machine::backend::Backend as StateBackend; use state_machine::ChangesTrieStorage as StateChangesTrieStorage; use hash_db::Hasher; @@ -73,9 +74,8 @@ pub trait BlockImportOperation where state: NewBlockState, ) -> error::Result<()>; - /// Append authorities set to the transaction. This is a set of parent block (set which - /// has been used to check justification of this block). - fn update_authorities(&mut self, authorities: Vec>); + /// Update cached data. + fn update_cache(&mut self, cache: HashMap, Vec>); /// Inject storage data into the database. fn update_db_storage(&mut self, update: >::Transaction) -> error::Result<()>; /// Inject storage data into the database replacing any existing data. diff --git a/substrate/core/client/src/blockchain.rs b/substrate/core/client/src/blockchain.rs index a18c6e5d57..682d686bd5 100644 --- a/substrate/core/client/src/blockchain.rs +++ b/substrate/core/client/src/blockchain.rs @@ -16,7 +16,9 @@ //! Substrate blockchain trait -use runtime_primitives::traits::{AuthorityIdFor, Block as BlockT, Header as HeaderT, NumberFor}; +use std::sync::Arc; + +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor}; use runtime_primitives::generic::BlockId; use runtime_primitives::Justification; @@ -78,7 +80,7 @@ pub trait Backend: HeaderBackend { /// Get last finalized block hash. fn last_finalized(&self) -> Result; /// Returns data cache reference, if it is enabled on this backend. - fn cache(&self) -> Option<&Cache>; + fn cache(&self) -> Option>>; /// Returns hashes of all blocks that are leaves of the block tree. /// in other words, that have no children, are chain heads. @@ -89,10 +91,16 @@ pub trait Backend: HeaderBackend { fn children(&self, parent_hash: Block::Hash) -> Result>; } +/// Provides access to the optional cache. +pub trait ProvideCache { + /// Returns data cache reference, if it is enabled on this backend. + fn cache(&self) -> Option>>; +} + /// Blockchain optional data cache. pub trait Cache: Send + Sync { - /// Returns the set of authorities, that was active at given block or None if there's no entry in the cache. - fn authorities_at(&self, block: BlockId) -> Option>>; + /// Returns cached value by the given key. + fn get_at(&self, key: &[u8], block: &BlockId) -> Option>; } /// Blockchain info diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs index 86d643d3b7..7ab66cbc09 100644 --- a/substrate/core/client/src/client.rs +++ b/substrate/core/client/src/client.rs @@ -16,7 +16,7 @@ //! Substrate Client -use std::{marker::PhantomData, collections::{HashSet, BTreeMap}, sync::Arc, panic::UnwindSafe, result}; +use std::{marker::PhantomData, collections::{HashSet, BTreeMap, HashMap}, sync::Arc, panic::UnwindSafe, result}; use crate::error::Error; use futures::sync::mpsc; use parking_lot::{Mutex, RwLock}; @@ -31,7 +31,7 @@ use consensus::{ }; use runtime_primitives::traits::{ Block as BlockT, Header as HeaderT, Zero, As, NumberFor, CurrentHeight, BlockNumberToHash, - ApiRef, ProvideRuntimeApi, Digest, DigestItem, AuthorityIdFor + ApiRef, ProvideRuntimeApi, Digest, DigestItem }; use runtime_primitives::BuildStorage; use crate::runtime_api::{CallRuntimeAt, ConstructRuntimeApi}; @@ -49,7 +49,8 @@ use hash_db::Hasher; use crate::backend::{self, BlockImportOperation, PrunableStateChangesTrieStorage}; use crate::blockchain::{ - self, Info as ChainInfo, Backend as ChainBackend, HeaderBackend as ChainHeaderBackend + self, Info as ChainInfo, Backend as ChainBackend, HeaderBackend as ChainHeaderBackend, + ProvideCache, Cache, }; use crate::call_executor::{CallExecutor, LocalCallExecutor}; use executor::{RuntimeVersion, RuntimeInfo}; @@ -342,16 +343,6 @@ impl Client where .expect("None is returned if there's no value stored for the given key; ':code' key is always defined; qed").0) } - /// Get the set of authorities at a given block. - pub fn authorities_at(&self, id: &BlockId) -> error::Result>> { - match self.backend.blockchain().cache().and_then(|cache| cache.authorities_at(*id)) { - Some(cached_value) => Ok(cached_value), - None => self.executor.call(id, "Core_authorities", &[], ExecutionStrategy::NativeElseWasm, NeverOffchainExt::new()) - .and_then(|r| Vec::>::decode(&mut &r[..]) - .ok_or_else(|| error::ErrorKind::InvalidAuthoritiesSet.into())) - } - } - /// Get the RuntimeVersion at a given block. pub fn runtime_version_at(&self, id: &BlockId) -> error::Result { self.executor.runtime_version(id) @@ -681,7 +672,7 @@ impl Client where &self, operation: &mut ClientImportOperation, import_block: ImportBlock, - new_authorities: Option>>, + new_cache: HashMap, Vec>, ) -> error::Result where E: CallExecutor + Send + Sync + Clone, { @@ -729,7 +720,7 @@ impl Client where import_headers, justification, body, - new_authorities, + new_cache, finalized, auxiliary, fork_choice, @@ -752,7 +743,7 @@ impl Client where import_headers: PrePostHeader, justification: Option, body: Option>, - authorities: Option>>, + new_cache: HashMap, Vec>, finalized: bool, aux: Vec<(Vec, Option>)>, fork_choice: ForkChoiceStrategy, @@ -810,9 +801,7 @@ impl Client where leaf_state, )?; - if let Some(authorities) = authorities { - operation.op.update_authorities(authorities); - } + operation.op.update_cache(new_cache); if let Some(storage_update) = storage_update { operation.op.update_db_storage(storage_update)?; } @@ -1324,6 +1313,15 @@ impl ChainHeaderBackend for Client wher } } +impl ProvideCache for Client where + B: backend::Backend, + Block: BlockT, +{ + fn cache(&self) -> Option>> { + self.backend.blockchain().cache() + } +} + impl ProvideRuntimeApi for Client where B: backend::Backend, E: CallExecutor + Clone + Send + Sync, @@ -1398,10 +1396,10 @@ impl consensus::BlockImport for Client fn import_block( &self, import_block: ImportBlock, - new_authorities: Option>>, + new_cache: HashMap, Vec>, ) -> Result { self.lock_import_and_run(|operation| { - self.apply_block(operation, import_block, new_authorities) + self.apply_block(operation, import_block, new_cache) }).map_err(|e| ConsensusErrorKind::ClientImport(e.to_string()).into()) } @@ -1431,17 +1429,6 @@ impl consensus::BlockImport for Client } } -impl consensus::Authorities for Client where - B: backend::Backend, - E: CallExecutor + Clone, - Block: BlockT, -{ - type Error = Error; - fn authorities(&self, at: &BlockId) -> Result>, Self::Error> { - self.authorities_at(at).map_err(|e| e.into()) - } -} - impl CurrentHeight for Client where B: backend::Backend, E: CallExecutor, @@ -1550,7 +1537,7 @@ pub(crate) mod tests { use primitives::twox_128; use runtime_primitives::traits::DigestItem as DigestItemT; use runtime_primitives::generic::DigestItem; - use test_client::{self, TestClient, AccountKeyring, AuthorityKeyring}; + use test_client::{self, TestClient, AccountKeyring}; use consensus::BlockOrigin; use test_client::client::backend::Backend as TestBackend; use test_client::BlockBuilderExt; @@ -1649,18 +1636,6 @@ pub(crate) mod tests { ); } - #[test] - fn authorities_call_works() { - let client = test_client::new(); - - assert_eq!(client.info().unwrap().chain.best_number, 0); - assert_eq!(client.authorities_at(&BlockId::Number(0)).unwrap(), vec![ - AuthorityKeyring::Alice.into(), - AuthorityKeyring::Bob.into(), - AuthorityKeyring::Charlie.into() - ]); - } - #[test] fn block_builder_works_with_no_transactions() { let client = test_client::new(); @@ -1705,15 +1680,6 @@ pub(crate) mod tests { ); } - #[test] - fn client_uses_authorities_from_blockchain_cache() { - let client = test_client::new_light(); - let genesis_hash = client.header(&BlockId::Number(0)).unwrap().unwrap().hash(); - // authorities cache is first filled in genesis block - // => should be read from cache here (remote request will fail in this test) - assert!(!client.authorities_at(&BlockId::Hash(genesis_hash)).unwrap().is_empty()); - } - #[test] fn block_builder_does_not_include_invalid() { let client = test_client::new(); diff --git a/substrate/core/client/src/error.rs b/substrate/core/client/src/error.rs index edc179ec6a..3ee3c0e2a1 100644 --- a/substrate/core/client/src/error.rs +++ b/substrate/core/client/src/error.rs @@ -62,12 +62,6 @@ error_chain! { display("Blockchain: {}", e), } - /// Invalid authorities set received from the runtime. - InvalidAuthoritiesSet { - description("authorities set is invalid"), - display("Current state of blockchain has invalid authorities set"), - } - /// Could not get runtime version. VersionInvalid { description("Runtime version error"), diff --git a/substrate/core/client/src/in_mem.rs b/substrate/core/client/src/in_mem.rs index 7f3cdfd8dd..d272135628 100644 --- a/substrate/core/client/src/in_mem.rs +++ b/substrate/core/client/src/in_mem.rs @@ -22,7 +22,7 @@ use parking_lot::RwLock; use primitives::{ChangesTrieConfiguration, storage::well_known_keys}; use runtime_primitives::generic::BlockId; use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, - NumberFor, As, Digest, DigestItem, AuthorityIdFor}; + NumberFor, As, Digest, DigestItem}; use runtime_primitives::{Justification, StorageOverlay, ChildrenStorageOverlay}; use state_machine::backend::{Backend as StateBackend, InMemory, Consolidate}; use state_machine::{self, InMemoryChangesTrieStorage, ChangesTrieAnchorBlockId}; @@ -104,12 +104,6 @@ struct BlockchainStorage { /// In-memory blockchain. Supports concurrent reads. pub struct Blockchain { storage: Arc>>, - cache: Cache, -} - -struct Cache { - storage: Arc>>, - authorities_at: RwLock>>>>, } impl Clone for Blockchain { @@ -117,10 +111,6 @@ impl Clone for Blockchain { let storage = Arc::new(RwLock::new(self.storage.read().clone())); Blockchain { storage: storage.clone(), - cache: Cache { - storage, - authorities_at: RwLock::new(self.cache.authorities_at.read().clone()), - }, } } } @@ -152,10 +142,6 @@ impl Blockchain { })); Blockchain { storage: storage.clone(), - cache: Cache { - storage: storage, - authorities_at: Default::default(), - }, } } @@ -355,8 +341,8 @@ impl blockchain::Backend for Blockchain { Ok(self.storage.read().finalized_hash.clone()) } - fn cache(&self) -> Option<&blockchain::Cache> { - Some(&self.cache) + fn cache(&self) -> Option>> { + None } fn leaves(&self) -> error::Result> { @@ -368,6 +354,12 @@ impl blockchain::Backend for Blockchain { } } +impl blockchain::ProvideCache for Blockchain { + fn cache(&self) -> Option>> { + None + } +} + impl backend::AuxStore for Blockchain { fn insert_aux< 'a, @@ -398,16 +390,12 @@ impl light::blockchain::Storage for Blockchain fn import_header( &self, header: Block::Header, - authorities: Option>>, + _cache: HashMap, Vec>, state: NewBlockState, aux_ops: Vec<(Vec, Option>)>, ) -> error::Result<()> { let hash = header.hash(); - let parent_hash = *header.parent_hash(); self.insert(hash, header, None, None, state)?; - if state.is_best() { - self.cache.insert(parent_hash, authorities); - } self.write_aux(aux_ops); Ok(()) @@ -435,15 +423,15 @@ impl light::blockchain::Storage for Blockchain .ok_or_else(|| error::ErrorKind::Backend(format!("Changes trie CHT for block {} not exists", block)).into()) } - fn cache(&self) -> Option<&blockchain::Cache> { - Some(&self.cache) + fn cache(&self) -> Option>> { + None } } /// In-memory operation. pub struct BlockImportOperation { pending_block: Option>, - pending_authorities: Option>>, + pending_cache: HashMap, Vec>, old_state: InMemory, new_state: Option>, changes_trie_update: Option>, @@ -480,8 +468,8 @@ where Ok(()) } - fn update_authorities(&mut self, authorities: Vec>) { - self.pending_authorities = Some(authorities); + fn update_cache(&mut self, cache: HashMap, Vec>) { + self.pending_cache = cache; } fn update_db_storage(&mut self, update: as StateBackend>::Transaction) -> error::Result<()> { @@ -602,7 +590,7 @@ where let old_state = self.state_at(BlockId::Hash(Default::default()))?; Ok(BlockImportOperation { pending_block: None, - pending_authorities: None, + pending_cache: Default::default(), old_state, new_state: None, changes_trie_update: None, @@ -629,7 +617,6 @@ where let (header, body, justification) = pending_block.block.into_inner(); let hash = header.hash(); - let parent_hash = *header.parent_hash(); self.states.write().insert(hash, operation.new_state.unwrap_or_else(|| old_state.clone())); @@ -642,10 +629,6 @@ where } self.blockchain.insert(hash, header, justification, body, pending_block.state)?; - // dumb implementation - store value for each block - if pending_block.state.is_best() { - self.blockchain.cache.insert(parent_hash, operation.pending_authorities); - } } if !operation.aux.is_empty() { @@ -710,23 +693,6 @@ where } } -impl Cache { - fn insert(&self, at: Block::Hash, authorities: Option>>) { - self.authorities_at.write().insert(at, authorities); - } -} - -impl blockchain::Cache for Cache { - fn authorities_at(&self, block: BlockId) -> Option>> { - let hash = match block { - BlockId::Hash(hash) => hash, - BlockId::Number(number) => self.storage.read().hashes.get(&number).cloned()?, - }; - - self.authorities_at.read().get(&hash).cloned().unwrap_or(None) - } -} - /// Prunable in-memory changes trie storage. pub struct ChangesTrieStorage(InMemoryChangesTrieStorage) where H::Out: HeapSizeOf; impl backend::PrunableStateChangesTrieStorage for ChangesTrieStorage where H::Out: HeapSizeOf { @@ -747,15 +713,6 @@ impl state_machine::ChangesTrieStorage for ChangesTrieStorage w } } -/// Insert authorities entry into in-memory blockchain cache. Extracted as a separate function to use it in tests. -pub fn cache_authorities_at( - blockchain: &Blockchain, - at: Block::Hash, - authorities: Option>> -) { - blockchain.cache.insert(at, authorities); -} - /// Check that genesis storage is valid. pub fn check_genesis_storage(top: &StorageOverlay, children: &ChildrenStorageOverlay) -> error::Result<()> { if top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) { diff --git a/substrate/core/client/src/light/backend.rs b/substrate/core/client/src/light/backend.rs index a00d42e673..c62a89ce9d 100644 --- a/substrate/core/client/src/light/backend.rs +++ b/substrate/core/client/src/light/backend.rs @@ -24,7 +24,7 @@ use parking_lot::RwLock; use runtime_primitives::{generic::BlockId, Justification, StorageOverlay, ChildrenStorageOverlay}; use state_machine::{Backend as StateBackend, TrieBackend, backend::InMemory as InMemoryState}; -use runtime_primitives::traits::{Block as BlockT, NumberFor, AuthorityIdFor, Zero, Header}; +use runtime_primitives::traits::{Block as BlockT, NumberFor, Zero, Header}; use crate::in_mem::{self, check_genesis_storage}; use crate::backend::{AuxStore, Backend as ClientBackend, BlockImportOperation, RemoteBackend, NewBlockState}; use crate::blockchain::HeaderBackend as BlockchainHeaderBackend; @@ -46,7 +46,7 @@ pub struct Backend { /// Light block (header and justification) import operation. pub struct ImportOperation { header: Option, - authorities: Option>>, + cache: HashMap, Vec>, leaf_state: NewBlockState, aux_ops: Vec<(Vec, Option>)>, finalized_blocks: Vec>, @@ -117,7 +117,7 @@ impl ClientBackend for Backend where fn begin_operation(&self) -> ClientResult { Ok(ImportOperation { header: None, - authorities: None, + cache: Default::default(), leaf_state: NewBlockState::Normal, aux_ops: Vec::new(), finalized_blocks: Vec::new(), @@ -146,7 +146,7 @@ impl ClientBackend for Backend where let is_genesis_import = header.number().is_zero(); self.blockchain.storage().import_header( header, - operation.authorities, + operation.cache, operation.leaf_state, operation.aux_ops, )?; @@ -254,8 +254,8 @@ where Ok(()) } - fn update_authorities(&mut self, authorities: Vec>) { - self.authorities = Some(authorities); + fn update_cache(&mut self, cache: HashMap, Vec>) { + self.cache = cache; } fn update_db_storage(&mut self, _update: >::Transaction) -> ClientResult<()> { diff --git a/substrate/core/client/src/light/blockchain.rs b/substrate/core/client/src/light/blockchain.rs index add183f67d..cf6304253a 100644 --- a/substrate/core/client/src/light/blockchain.rs +++ b/substrate/core/client/src/light/blockchain.rs @@ -17,16 +17,16 @@ //! Light client blockchin backend. Only stores headers and justifications of recent //! blocks. CHT roots are stored for headers of ancient blocks. -use std::sync::Weak; +use std::{sync::{Weak, Arc}, collections::HashMap}; use futures::{Future, IntoFuture}; use parking_lot::Mutex; use runtime_primitives::{Justification, generic::BlockId}; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero, AuthorityIdFor}; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}; use crate::backend::{AuxStore, NewBlockState}; use crate::blockchain::{Backend as BlockchainBackend, BlockStatus, Cache as BlockchainCache, - HeaderBackend as BlockchainHeaderBackend, Info as BlockchainInfo}; + HeaderBackend as BlockchainHeaderBackend, Info as BlockchainInfo, ProvideCache}; use crate::cht; use crate::error::{ErrorKind as ClientErrorKind, Result as ClientResult}; use crate::light::fetcher::{Fetcher, RemoteHeaderRequest}; @@ -40,7 +40,7 @@ pub trait Storage: AuxStore + BlockchainHeaderBackend { fn import_header( &self, header: Block::Header, - authorities: Option>>, + cache: HashMap, Vec>, state: NewBlockState, aux_ops: Vec<(Vec, Option>)>, ) -> ClientResult<()>; @@ -61,7 +61,7 @@ pub trait Storage: AuxStore + BlockchainHeaderBackend { fn changes_trie_cht_root(&self, cht_size: u64, block: NumberFor) -> ClientResult; /// Get storage cache. - fn cache(&self) -> Option<&BlockchainCache>; + fn cache(&self) -> Option>>; } /// Light client blockchain. @@ -156,7 +156,7 @@ impl BlockchainBackend for Blockchain where Block: Blo self.storage.last_finalized() } - fn cache(&self) -> Option<&BlockchainCache> { + fn cache(&self) -> Option>> { self.storage.cache() } @@ -169,6 +169,12 @@ impl BlockchainBackend for Blockchain where Block: Blo } } +impl, F, Block: BlockT> ProvideCache for Blockchain { + fn cache(&self) -> Option>> { + self.storage.cache() + } +} + #[cfg(test)] pub mod tests { use std::collections::HashMap; @@ -246,7 +252,7 @@ pub mod tests { fn import_header( &self, _header: Header, - _authorities: Option>>, + _cache: HashMap, Vec>, _state: NewBlockState, _aux_ops: Vec<(Vec, Option>)>, ) -> ClientResult<()> { @@ -278,7 +284,7 @@ pub mod tests { ).into()) } - fn cache(&self) -> Option<&BlockchainCache> { + fn cache(&self) -> Option>> { None } } diff --git a/substrate/core/client/src/light/call_executor.rs b/substrate/core/client/src/light/call_executor.rs index b2e4c45a7d..1e50d3398e 100644 --- a/substrate/core/client/src/light/call_executor.rs +++ b/substrate/core/client/src/light/call_executor.rs @@ -512,7 +512,7 @@ mod tests { } // check method that doesn't requires environment - let (remote, local) = execute(&remote_client, 0, "Core_authorities"); + let (remote, local) = execute(&remote_client, 0, "Core_version"); assert_eq!(remote, local); // check method that requires environment diff --git a/substrate/core/client/src/light/fetcher.rs b/substrate/core/client/src/light/fetcher.rs index 893682d5f8..4cbbc819b3 100644 --- a/substrate/core/client/src/light/fetcher.rs +++ b/substrate/core/client/src/light/fetcher.rs @@ -390,6 +390,7 @@ impl<'a, H, Number, Hash> ChangesTrieRootsStorage for RootsStorage<'a, Number pub mod tests { use futures::future::{ok, err, FutureResult}; use parking_lot::Mutex; + use parity_codec::Decode; use crate::client::tests::prepare_client_with_key_changes; use executor::{self, NativeExecutionDispatch}; use crate::error::Error as ClientError; @@ -436,7 +437,7 @@ pub mod tests { type TestChecker = LightDataChecker, Blake2Hasher, Block, DummyStorage, OkCallFetcher>; - fn prepare_for_read_proof_check() -> (TestChecker, Header, Vec>, usize) { + fn prepare_for_read_proof_check() -> (TestChecker, Header, Vec>, u32) { // prepare remote client let remote_client = test_client::new(); let remote_block_id = BlockId::Number(0); @@ -445,7 +446,9 @@ pub mod tests { remote_block_header.state_root = remote_client.state_at(&remote_block_id).unwrap().storage_root(::std::iter::empty()).0.into(); // 'fetch' read proof from remote node - let authorities_len = remote_client.authorities_at(&remote_block_id).unwrap().len(); + let authorities_len = remote_client.storage(&remote_block_id, &StorageKey(well_known_keys::AUTHORITY_COUNT.to_vec())) + .unwrap() + .and_then(|v| Decode::decode(&mut &v.0[..])).unwrap(); let remote_read_proof = remote_client.read_proof(&remote_block_id, well_known_keys::AUTHORITY_COUNT).unwrap(); // check remote read proof locally diff --git a/substrate/core/client/src/runtime_api.rs b/substrate/core/client/src/runtime_api.rs index a912b8c576..6bc43ab270 100644 --- a/substrate/core/client/src/runtime_api.rs +++ b/substrate/core/client/src/runtime_api.rs @@ -38,7 +38,6 @@ use rstd::result; pub use parity_codec::{Encode, Decode}; #[cfg(feature = "std")] use crate::error; -use rstd::vec::Vec; use sr_api_macros::decl_runtime_apis; use primitives::OpaqueMetadata; #[cfg(feature = "std")] @@ -112,13 +111,11 @@ pub trait CallRuntimeAt { } decl_runtime_apis! { - /// The `Core` api trait that is mandantory for each runtime. + /// The `Core` api trait that is mandatory for each runtime. #[core_trait] pub trait Core { /// Returns the version of the runtime. fn version() -> RuntimeVersion; - /// Returns the authorities. - fn authorities() -> Vec>; /// Execute the given block. fn execute_block(block: Block); /// Initialize a block with the given header. diff --git a/substrate/core/consensus/aura/Cargo.toml b/substrate/core/consensus/aura/Cargo.toml index ca4f139ee4..190ace413d 100644 --- a/substrate/core/consensus/aura/Cargo.toml +++ b/substrate/core/consensus/aura/Cargo.toml @@ -25,6 +25,7 @@ parking_lot = "0.7.1" error-chain = "0.12" log = "0.4" consensus_common = { package = "substrate-consensus-common", path = "../common" } +authorities = { package = "substrate-consensus-authorities", path = "../authorities" } [dev-dependencies] keyring = { package = "substrate-keyring", path = "../../keyring" } diff --git a/substrate/core/consensus/aura/src/lib.rs b/substrate/core/consensus/aura/src/lib.rs index cf83d3fa6e..d3ea731795 100644 --- a/substrate/core/consensus/aura/src/lib.rs +++ b/substrate/core/consensus/aura/src/lib.rs @@ -29,21 +29,23 @@ use std::{sync::Arc, time::Duration, thread, marker::PhantomData, hash::Hash, fmt::Debug}; use parity_codec::{Encode, Decode}; -use consensus_common::{ - Authorities, BlockImport, Environment, Proposer, ForkChoiceStrategy +use consensus_common::{self, Authorities, BlockImport, Environment, Proposer, + ForkChoiceStrategy, ImportBlock, BlockOrigin, Error as ConsensusError, }; +use consensus_common::well_known_cache_keys; use consensus_common::import_queue::{Verifier, BasicQueue, SharedBlockImport, SharedJustificationImport}; use client::ChainHead; use client::block_builder::api::BlockBuilder as BlockBuilderApi; +use client::blockchain::ProvideCache; use client::runtime_api::ApiExt; -use consensus_common::{ImportBlock, BlockOrigin}; use aura_primitives::AURA_ENGINE_ID; use runtime_primitives::{generic, generic::BlockId, Justification}; use runtime_primitives::traits::{ - Block, Header, Digest, DigestItemFor, DigestItem, ProvideRuntimeApi + Block, Header, Digest, DigestItemFor, DigestItem, ProvideRuntimeApi, AuthorityIdFor, }; use primitives::Pair; use inherents::{InherentDataProviders, InherentData, RuntimeString}; +use authorities::AuthoritiesApi; use futures::{Stream, Future, IntoFuture, future}; use tokio::timer::Timeout; @@ -179,12 +181,13 @@ pub fn start_aura_thread( force_authoring: bool, ) -> Result<(), consensus_common::Error> where B: Block + 'static, - C: Authorities + ChainHead + Send + Sync + 'static, + C: ChainHead + ProvideRuntimeApi + ProvideCache + Send + Sync + 'static, + C::Api: AuthoritiesApi, E: Environment + Send + Sync + 'static, E::Proposer: Proposer + Send + 'static, <>::Create as IntoFuture>::Future: Send + 'static, I: BlockImport + Send + Sync + 'static, - Error: From + From + 'static, + Error: From + 'static, P: Pair + Send + Sync + 'static, P::Public: Encode + Decode + Eq + Clone + Debug + Hash + Send + Sync + 'static, P::Signature: Encode, @@ -226,12 +229,13 @@ pub fn start_aura( force_authoring: bool, ) -> Result, consensus_common::Error> where B: Block, - C: Authorities + ChainHead, + C: ChainHead + ProvideRuntimeApi + ProvideCache, + C::Api: AuthoritiesApi, E: Environment, E::Proposer: Proposer, <>::Create as IntoFuture>::Future: Send + 'static, I: BlockImport + Send + Sync + 'static, - Error: From + From, + Error: From, P: Pair + Send + Sync + 'static, P::Public: Hash + Eq + Send + Sync + Clone + Debug + Encode + Decode + 'static, P::Signature: Encode, @@ -270,7 +274,8 @@ struct AuraWorker { } impl SlotWorker for AuraWorker where - C: Authorities, + C: ProvideRuntimeApi + ProvideCache, + C::Api: AuthoritiesApi, E: Environment, E::Proposer: Proposer, <>::Create as IntoFuture>::Future: Send + 'static, @@ -278,7 +283,7 @@ impl SlotWorker for AuraWorker + From, + Error: From, SO: SyncOracle + Send + Clone, DigestItemFor: CompatibleDigestItem

+ DigestItem>, Error: ::std::error::Error + Send + 'static + From<::consensus_common::Error>, @@ -306,7 +311,7 @@ impl SlotWorker for AuraWorker authorities, Err(e) => { warn!( @@ -418,7 +423,7 @@ impl SlotWorker for AuraWorker ?pre_hash ); - if let Err(e) = block_import.import_block(import_block, None) { + if let Err(e) = block_import.import_block(import_block, Default::default()) { warn!(target: "aura", "Error with block built on {:?}: {:?}", parent_hash, e); telemetry!(CONSENSUS_WARN; "aura.err_with_block_built_on"; @@ -573,13 +578,14 @@ impl ExtraVerification for NothingExtra { #[forbid(deprecated)] impl Verifier for AuraVerifier where - C: Authorities + ProvideRuntimeApi + Send + Sync, + C: ProvideRuntimeApi + Send + Sync, C::Api: BlockBuilderApi, DigestItemFor: CompatibleDigestItem

+ DigestItem>, E: ExtraVerification, P: Pair + Send + Sync + 'static, P::Public: Send + Sync + Hash + Eq + Clone + Decode + Encode + Debug + AsRef + 'static, P::Signature: Encode + Decode, + Self: Authorities, { fn verify( &self, @@ -593,7 +599,7 @@ impl Verifier for AuraVerifier where .map_err(|e| format!("Could not extract timestamp and slot: {:?}", e))?; let hash = header.hash(); let parent_hash = *header.parent_hash(); - let authorities = self.client.authorities(&BlockId::Hash(parent_hash)) + let authorities = self.authorities(&BlockId::Hash(parent_hash)) .map_err(|e| format!("Could not fetch authorities at {:?}: {:?}", parent_hash, e))?; let extra_verification = self.extra.verify( @@ -669,6 +675,31 @@ impl Verifier for AuraVerifier where } } +impl Authorities for AuraVerifier where + B: Block, + C: ProvideRuntimeApi + ProvideCache, + C::Api: AuthoritiesApi, +{ + type Error = ConsensusError; + + fn authorities(&self, at: &BlockId) -> Result>, Self::Error> { + authorities(self.client.as_ref(), at) + } +} + +fn authorities(client: &C, at: &BlockId) -> Result>, ConsensusError> where + B: Block, + C: ProvideRuntimeApi + ProvideCache, + C::Api: AuthoritiesApi, +{ + client + .cache() + .and_then(|cache| cache.get_at(well_known_cache_keys::AUTHORITIES, at) + .and_then(|v| Decode::decode(&mut &v[..]))) + .or_else(|| client.runtime_api().authorities(at).ok()) + .ok_or_else(|| consensus_common::ErrorKind::InvalidAuthoritiesSet.into()) +} + /// The Aura import queue type. pub type AuraImportQueue = BasicQueue; @@ -697,8 +728,8 @@ pub fn import_queue( allow_old_seals: bool, ) -> Result, consensus_common::Error> where B: Block, - C: 'static + Authorities + ProvideRuntimeApi + Send + Sync, - C::Api: BlockBuilderApi, + C: 'static + ProvideRuntimeApi + ProvideCache + Send + Sync, + C::Api: BlockBuilderApi + AuthoritiesApi, DigestItemFor: CompatibleDigestItem

+ DigestItem>, E: 'static + ExtraVerification, P: Pair + Send + Sync + 'static, @@ -889,4 +920,16 @@ mod tests { runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap(); } + + #[test] + fn authorities_call_works() { + let client = test_client::new(); + + assert_eq!(client.info().unwrap().chain.best_number, 0); + assert_eq!(authorities(&client, &BlockId::Number(0)).unwrap(), vec![ + Keyring::Alice.into(), + Keyring::Bob.into(), + Keyring::Charlie.into() + ]); + } } diff --git a/substrate/core/consensus/authorities/Cargo.toml b/substrate/core/consensus/authorities/Cargo.toml new file mode 100644 index 0000000000..0800656f76 --- /dev/null +++ b/substrate/core/consensus/authorities/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "substrate-consensus-authorities" +version = "0.1.0" +authors = ["Parity Technologies "] +description = "Primitives for Aura consensus" +edition = "2018" + +[dependencies] +parity-codec = { version = "3.0", default-features = false } +substrate-client = { path = "../../client", default-features = false } +primitives = { package = "substrate-primitives", path = "../../primitives", default-features = false } +runtime_support = { package = "srml-support", path = "../../../srml/support", default-features = false } +runtime_primitives = { package = "sr-primitives", path = "../../sr-primitives", default-features = false } +sr-version = { path = "../../sr-version", default-features = false } +runtime_io = { package = "sr-io", path = "../../sr-io", default-features = false } +rstd = { package = "sr-std", path = "../../sr-std", default-features = false } + +[features] +default = ["std"] +std = [ + "parity-codec/std", + "substrate-client/std", + "primitives/std", + "runtime_support/std", + "runtime_primitives/std", + "sr-version/std", + "runtime_io/std", + "rstd/std" +] diff --git a/substrate/core/consensus/authorities/src/lib.rs b/substrate/core/consensus/authorities/src/lib.rs new file mode 100644 index 0000000000..a5ad974f5b --- /dev/null +++ b/substrate/core/consensus/authorities/src/lib.rs @@ -0,0 +1,31 @@ +// Copyright 2019 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 . + +//! Authorities API. + +#![cfg_attr(not(feature = "std"), no_std)] + +use substrate_client::decl_runtime_apis; +use runtime_primitives::traits::AuthorityIdFor; +use rstd::vec::Vec; + +decl_runtime_apis! { + /// Authorities API. + pub trait AuthoritiesApi { + /// Returns the authorities at the given block. + fn authorities() -> Vec>; + } +} diff --git a/substrate/core/consensus/common/src/block_import.rs b/substrate/core/consensus/common/src/block_import.rs index 06c78d74af..a4a765da4e 100644 --- a/substrate/core/consensus/common/src/block_import.rs +++ b/substrate/core/consensus/common/src/block_import.rs @@ -16,9 +16,10 @@ //! Block import helpers. -use runtime_primitives::traits::{AuthorityIdFor, Block as BlockT, DigestItemFor, Header as HeaderT, NumberFor}; +use runtime_primitives::traits::{Block as BlockT, DigestItemFor, Header as HeaderT, NumberFor}; use runtime_primitives::Justification; use std::borrow::Cow; +use std::collections::HashMap; /// Block import result. #[derive(Debug, PartialEq, Eq)] @@ -175,11 +176,13 @@ pub trait BlockImport { parent_hash: B::Hash, ) -> Result; - /// Import a Block alongside the new authorities valid from this block forward + /// Import a block. + /// + /// Cached data can be accessed through the blockchain cache. fn import_block( &self, block: ImportBlock, - new_authorities: Option>>, + cache: HashMap, Vec>, ) -> Result; } diff --git a/substrate/core/consensus/common/src/error.rs b/substrate/core/consensus/common/src/error.rs index 58362b8e80..0f1914087b 100644 --- a/substrate/core/consensus/common/src/error.rs +++ b/substrate/core/consensus/common/src/error.rs @@ -58,6 +58,12 @@ error_chain! { display("Message signature {:?} by {:?} is invalid.", s, a), } + /// Invalid authorities set received from the runtime. + InvalidAuthoritiesSet { + description("authorities set is invalid"), + display("Current state of blockchain has invalid authorities set"), + } + /// Account is not an authority. InvalidAuthority(a: Public) { description("Message sender is not a valid authority"), diff --git a/substrate/core/consensus/common/src/import_queue.rs b/substrate/core/consensus/common/src/import_queue.rs index 2e1c29d4d8..1d0a52d324 100644 --- a/substrate/core/consensus/common/src/import_queue.rs +++ b/substrate/core/consensus/common/src/import_queue.rs @@ -28,6 +28,7 @@ use crate::block_import::{ BlockImport, BlockOrigin, ImportBlock, ImportedAux, ImportResult, JustificationImport, }; use crossbeam_channel::{self as channel, Receiver, Sender}; +use parity_codec::Encode; use std::sync::Arc; use std::thread; @@ -38,6 +39,7 @@ use runtime_primitives::traits::{ use runtime_primitives::Justification; use crate::error::Error as ConsensusError; +use parity_codec::alloc::collections::hash_map::HashMap; /// Shared block import struct used by the queue. pub type SharedBlockImport = Arc + Send + Sync>; @@ -566,7 +568,12 @@ pub fn import_single_block>( BlockImportError::VerificationFailed(peer.clone(), msg) })?; - import_error(import_handle.import_block(import_block, new_authorities)) + let mut cache = HashMap::new(); + if let Some(authorities) = new_authorities { + cache.insert(crate::well_known_cache_keys::AUTHORITIES.to_vec(), authorities.encode()); + } + + import_error(import_handle.import_block(import_block, cache)) } #[cfg(test)] diff --git a/substrate/core/consensus/common/src/lib.rs b/substrate/core/consensus/common/src/lib.rs index 73315ed7aa..c1308f2da9 100644 --- a/substrate/core/consensus/common/src/lib.rs +++ b/substrate/core/consensus/common/src/lib.rs @@ -53,7 +53,9 @@ pub use block_import::{ /// Trait for getting the authorities at a given block. pub trait Authorities { - type Error: ::std::error::Error + Send + 'static; /// Get the authorities at the given block. + type Error: std::error::Error + Send + 'static; + + /// Get the authorities at the given block. fn authorities(&self, at: &BlockId) -> Result>, Self::Error>; } @@ -115,3 +117,9 @@ impl SyncOracle for Arc { T::is_offline(&*self) } } + +/// A list of all well known keys in the cache. +pub mod well_known_cache_keys { + /// A list of authorities. + pub const AUTHORITIES: &'static [u8] = b"auth"; +} diff --git a/substrate/core/finality-grandpa/src/import.rs b/substrate/core/finality-grandpa/src/import.rs index 9afbe443f9..7914660e29 100644 --- a/substrate/core/finality-grandpa/src/import.rs +++ b/substrate/core/finality-grandpa/src/import.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use std::sync::Arc; +use std::{sync::Arc, collections::HashMap}; use log::{debug, trace, info}; use parity_codec::Encode; @@ -388,7 +388,7 @@ impl, RA, PRA> BlockImport { type Error = ConsensusError; - fn import_block(&self, mut block: ImportBlock, new_authorities: Option>) + fn import_block(&self, mut block: ImportBlock, new_cache: HashMap, Vec>) -> Result { let hash = block.post_header().hash(); @@ -406,8 +406,8 @@ impl, RA, PRA> BlockImport // we don't want to finalize on `inner.import_block` let mut justification = block.justification.take(); - let enacts_consensus_change = new_authorities.is_some(); - let import_result = self.inner.import_block(block, new_authorities); + let enacts_consensus_change = !new_cache.is_empty(); + let import_result = self.inner.import_block(block, new_cache); let mut imported_aux = { match import_result { diff --git a/substrate/core/finality-grandpa/src/tests.rs b/substrate/core/finality-grandpa/src/tests.rs index 582439419e..f0d5894bb6 100644 --- a/substrate/core/finality-grandpa/src/tests.rs +++ b/substrate/core/finality-grandpa/src/tests.rs @@ -277,16 +277,6 @@ impl Core for RuntimeApi { unimplemented!("Not required for testing!") } - fn authorities_runtime_api_impl( - &self, - _: &BlockId, - _: ExecutionContext, - _: Option<()>, - _: Vec, - ) -> Result>> { - unimplemented!("Not required for testing!") - } - fn execute_block_runtime_api_impl( &self, _: &BlockId, @@ -992,12 +982,12 @@ fn allows_reimporting_change_blocks() { }; assert_eq!( - block_import.import_block(block(), None).unwrap(), + block_import.import_block(block(), HashMap::new()).unwrap(), ImportResult::Imported(ImportedAux { needs_justification: true, clear_justification_requests: false, bad_justification: false }), ); assert_eq!( - block_import.import_block(block(), None).unwrap(), + block_import.import_block(block(), HashMap::new()).unwrap(), ImportResult::AlreadyInChain ); } @@ -1035,12 +1025,12 @@ fn test_bad_justification() { }; assert_eq!( - block_import.import_block(block(), None).unwrap(), + block_import.import_block(block(), HashMap::new()).unwrap(), ImportResult::Imported(ImportedAux { needs_justification: true, clear_justification_requests: false, bad_justification: true }), ); assert_eq!( - block_import.import_block(block(), None).unwrap(), + block_import.import_block(block(), HashMap::new()).unwrap(), ImportResult::AlreadyInChain ); } diff --git a/substrate/core/network/src/chain.rs b/substrate/core/network/src/chain.rs index 87c0dca9c1..92236e7c63 100644 --- a/substrate/core/network/src/chain.rs +++ b/substrate/core/network/src/chain.rs @@ -20,18 +20,13 @@ use client::{self, Client as SubstrateClient, ClientInfo, BlockStatus, CallExecu use client::error::Error; use client::light::fetcher::ChangesProof; use consensus::{BlockImport, Error as ConsensusError}; -use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, AuthorityIdFor}; +use runtime_primitives::traits::{Block as BlockT, Header as HeaderT}; use runtime_primitives::generic::{BlockId}; -use consensus::{ImportBlock, ImportResult}; use runtime_primitives::Justification; use primitives::{H256, Blake2Hasher, storage::StorageKey}; /// Local client abstraction for the network. pub trait Client: Send + Sync { - /// Import a new block. Parent is supposed to be existing in the blockchain. - fn import(&self, block: ImportBlock, new_authorities: Option>>) - -> Result; - /// Get blockchain info. fn info(&self) -> Result, Error>; @@ -80,12 +75,6 @@ impl Client for SubstrateClient where Block: BlockT, RA: Send + Sync { - fn import(&self, block: ImportBlock, new_authorities: Option>>) - -> Result - { - (self as &SubstrateClient).import_block(block, new_authorities) - } - fn info(&self) -> Result, Error> { (self as &SubstrateClient).info() } diff --git a/substrate/core/rpc/src/state/tests.rs b/substrate/core/rpc/src/state/tests.rs index f28e63b15d..6746685d36 100644 --- a/substrate/core/rpc/src/state/tests.rs +++ b/substrate/core/rpc/src/state/tests.rs @@ -221,7 +221,7 @@ fn should_return_runtime_version() { assert_eq!( ::serde_json::to_string(&api.runtime_version(None.into()).unwrap()).unwrap(), - r#"{"specName":"test","implName":"parity-test","authoringVersion":1,"specVersion":1,"implVersion":1,"apis":[["0xdf6acb689907609b",1],["0x37e397fc7c91f5e4",1],["0xd2bc9897eed08f15",1],["0x40fe3ad401f8959a",2],["0xc6e9a76309f39b09",1],["0xdd718d5cc53262d4",1],["0xf78b278be53f454c",1]]}"# + r#"{"specName":"test","implName":"parity-test","authoringVersion":1,"specVersion":1,"implVersion":1,"apis":[["0xdf6acb689907609b",1],["0x37e397fc7c91f5e4",1],["0xd2bc9897eed08f15",1],["0x40fe3ad401f8959a",2],["0xc6e9a76309f39b09",1],["0xdd718d5cc53262d4",1],["0xf78b278be53f454c",1],["0x7801759919ee83e5",1]]}"# ); } diff --git a/substrate/core/service/test/src/lib.rs b/substrate/core/service/test/src/lib.rs index 1735382bb2..66975d4f20 100644 --- a/substrate/core/service/test/src/lib.rs +++ b/substrate/core/service/test/src/lib.rs @@ -20,6 +20,7 @@ use std::iter; use std::sync::Arc; use std::net::Ipv4Addr; use std::time::Duration; +use std::collections::HashMap; use log::info; use futures::{Future, Stream}; use tempdir::TempDir; @@ -227,7 +228,7 @@ where info!("Generating #{}", i); } let import_data = block_factory(&first_service); - first_service.client().import_block(import_data, None).expect("Error importing test block"); + first_service.client().import_block(import_data, HashMap::new()).expect("Error importing test block"); } first_service.network().node_id().unwrap() }; diff --git a/substrate/core/sr-api-macros/tests/decl_and_impl.rs b/substrate/core/sr-api-macros/tests/decl_and_impl.rs index c5ef8cbb3e..a8b3156123 100644 --- a/substrate/core/sr-api-macros/tests/decl_and_impl.rs +++ b/substrate/core/sr-api-macros/tests/decl_and_impl.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use runtime_primitives::traits::{GetNodeBlockType, Block as BlockT, AuthorityIdFor}; +use runtime_primitives::traits::{GetNodeBlockType, Block as BlockT}; use runtime_primitives::generic::BlockId; use client::runtime_api::{self, RuntimeApiInfo}; use client::{error::Result, decl_runtime_apis, impl_runtime_apis}; @@ -68,9 +68,6 @@ impl_runtime_apis! { fn version() -> runtime_api::RuntimeVersion { unimplemented!() } - fn authorities() -> Vec> { - unimplemented!() - } fn execute_block(_: Block) { unimplemented!() } diff --git a/substrate/core/test-client/src/client_ext.rs b/substrate/core/test-client/src/client_ext.rs index 511b55ff62..70e7feb078 100644 --- a/substrate/core/test-client/src/client_ext.rs +++ b/substrate/core/test-client/src/client_ext.rs @@ -22,6 +22,7 @@ use runtime_primitives::Justification; use runtime_primitives::generic::BlockId; use primitives::Blake2Hasher; use runtime; +use parity_codec::alloc::collections::hash_map::HashMap; /// Extension trait for a test client. pub trait TestClient: Sized { @@ -60,7 +61,7 @@ impl TestClient for Client fork_choice: ForkChoiceStrategy::LongestChain, }; - self.import_block(import, None).map(|_| ()) + self.import_block(import, HashMap::new()).map(|_| ()) } fn import_justified(&self, origin: BlockOrigin, block: runtime::Block, justification: Justification) @@ -77,7 +78,7 @@ impl TestClient for Client fork_choice: ForkChoiceStrategy::LongestChain, }; - self.import_block(import, None).map(|_| ()) + self.import_block(import, HashMap::new()).map(|_| ()) } fn finalize_block(&self, id: BlockId, justification: Option) -> client::error::Result<()> { diff --git a/substrate/core/test-runtime/Cargo.toml b/substrate/core/test-runtime/Cargo.toml index 6132cbbb1a..eb74eb68ab 100644 --- a/substrate/core/test-runtime/Cargo.toml +++ b/substrate/core/test-runtime/Cargo.toml @@ -23,6 +23,7 @@ runtime_support = { package = "srml-support", path = "../../srml/support", defau offchain-primitives = { package = "substrate-offchain-primitives", path = "../offchain/primitives", default-features = false} executive = { package = "srml-executive", path = "../../srml/executive", default-features = false } cfg-if = "0.1.6" +consensus_authorities = { package = "substrate-consensus-authorities", path = "../../core/consensus/authorities", default-features = false } [dev-dependencies] substrate-executor = { path = "../executor" } diff --git a/substrate/core/test-runtime/src/lib.rs b/substrate/core/test-runtime/src/lib.rs index 744bdd7fea..028bb7ba52 100644 --- a/substrate/core/test-runtime/src/lib.rs +++ b/substrate/core/test-runtime/src/lib.rs @@ -34,7 +34,7 @@ use runtime_primitives::{ create_runtime_str, traits::{ BlindCheckable, BlakeTwo256, Block as BlockT, Extrinsic as ExtrinsicT, - GetNodeBlockType, GetRuntimeBlockType, + GetNodeBlockType, GetRuntimeBlockType, AuthorityIdFor, }, }; use runtime_version::RuntimeVersion; @@ -289,10 +289,6 @@ cfg_if! { version() } - fn authorities() -> Vec { - system::authorities() - } - fn execute_block(block: Block) { system::execute_block(block) } @@ -386,6 +382,12 @@ cfg_if! { runtime_io::submit_extrinsic(&ex) } } + + impl consensus_authorities::AuthoritiesApi for Runtime { + fn authorities() -> Vec> { + crate::system::authorities() + } + } } } else { impl_runtime_apis! { @@ -394,10 +396,6 @@ cfg_if! { version() } - fn authorities() -> Vec { - system::authorities() - } - fn execute_block(block: Block) { system::execute_block(block) } @@ -496,6 +494,12 @@ cfg_if! { runtime_io::submit_extrinsic(&ex) } } + + impl consensus_authorities::AuthoritiesApi for Runtime { + fn authorities() -> Vec> { + crate::system::authorities() + } + } } } } \ No newline at end of file diff --git a/substrate/core/test-runtime/wasm/Cargo.lock b/substrate/core/test-runtime/wasm/Cargo.lock index ced5304a73..e3c211fa93 100644 --- a/substrate/core/test-runtime/wasm/Cargo.lock +++ b/substrate/core/test-runtime/wasm/Cargo.lock @@ -2284,6 +2284,20 @@ dependencies = [ "substrate-client 0.1.0", ] +[[package]] +name = "substrate-consensus-authorities" +version = "0.1.0" +dependencies = [ + "parity-codec 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 0.1.0", + "sr-primitives 0.1.0", + "sr-std 0.1.0", + "sr-version 0.1.0", + "srml-support 0.1.0", + "substrate-client 0.1.0", + "substrate-primitives 0.1.0", +] + [[package]] name = "substrate-consensus-common" version = "0.1.0" @@ -2453,6 +2467,7 @@ dependencies = [ "srml-support 0.1.0", "substrate-client 0.1.0", "substrate-consensus-aura-primitives 0.1.0", + "substrate-consensus-authorities 0.1.0", "substrate-inherents 0.1.0", "substrate-keyring 0.1.0", "substrate-offchain-primitives 0.1.0", diff --git a/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index 9113d509fd7675b9d6cbd3537cd64650e05bd43f..8f90cec76d61d5d4c596a975dc52d018a7d82f24 100644 GIT binary patch delta 4371 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 delta 4460 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 fteAG7bUg2~Wl0tks~DW Vec { - Consensus::authorities() - } - fn execute_block(block: Block) { Executive::execute_block(block) } @@ -292,4 +288,10 @@ impl_runtime_apis! { Executive::offchain_worker(n) } } + + impl consensus_authorities::AuthoritiesApi for Runtime { + fn authorities() -> Vec { + Consensus::authorities() + } + } } diff --git a/substrate/node-template/runtime/wasm/Cargo.lock b/substrate/node-template/runtime/wasm/Cargo.lock index b54048856d..824a8f3650 100644 --- a/substrate/node-template/runtime/wasm/Cargo.lock +++ b/substrate/node-template/runtime/wasm/Cargo.lock @@ -1273,6 +1273,7 @@ dependencies = [ "srml-timestamp 0.1.0", "substrate-client 0.1.0", "substrate-consensus-aura-primitives 0.1.0", + "substrate-consensus-authorities 0.1.0", "substrate-offchain-primitives 0.1.0", "substrate-primitives 0.1.0", ] @@ -2449,6 +2450,20 @@ dependencies = [ "substrate-client 0.1.0", ] +[[package]] +name = "substrate-consensus-authorities" +version = "0.1.0" +dependencies = [ + "parity-codec 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 0.1.0", + "sr-primitives 0.1.0", + "sr-std 0.1.0", + "sr-version 0.1.0", + "srml-support 0.1.0", + "substrate-client 0.1.0", + "substrate-primitives 0.1.0", +] + [[package]] name = "substrate-consensus-common" version = "0.1.0" diff --git a/substrate/node-template/src/service.rs b/substrate/node-template/src/service.rs index 95283c03f2..2b7554c79d 100644 --- a/substrate/node-template/src/service.rs +++ b/substrate/node-template/src/service.rs @@ -86,30 +86,32 @@ construct_service_factory! { FullImportQueue = AuraImportQueue< Self::Block, > - { |config: &mut FactoryFullConfiguration , client: Arc>| + { |config: &mut FactoryFullConfiguration , client: Arc>| { import_queue::<_, _, _, Pair>( - SlotDuration::get_or_compute(&*client)?, - client.clone(), - None, - client, - NothingExtra, - config.custom.inherent_data_providers.clone(), + SlotDuration::get_or_compute(&*client)?, + client.clone(), + None, + client, + NothingExtra, + config.custom.inherent_data_providers.clone(), true, - ).map_err(Into::into) + ).map_err(Into::into) + } }, LightImportQueue = AuraImportQueue< Self::Block, > - { |config: &mut FactoryFullConfiguration, client: Arc>| + { |config: &mut FactoryFullConfiguration, client: Arc>| { import_queue::<_, _, _, Pair>( - SlotDuration::get_or_compute(&*client)?, - client.clone(), - None, - client, - NothingExtra, - config.custom.inherent_data_providers.clone(), + SlotDuration::get_or_compute(&*client)?, + client.clone(), + None, + client, + NothingExtra, + config.custom.inherent_data_providers.clone(), true, - ).map_err(Into::into) + ).map_err(Into::into) + } }, } } diff --git a/substrate/node/runtime/Cargo.toml b/substrate/node/runtime/Cargo.toml index 6c0307d47e..3fe3adce30 100644 --- a/substrate/node/runtime/Cargo.toml +++ b/substrate/node/runtime/Cargo.toml @@ -37,6 +37,7 @@ rustc-hex = { version = "2.0", optional = true } hex-literal = { version = "0.1.0", optional = true } serde = { version = "1.0", optional = true } substrate-keyring = { path = "../../core/keyring", optional = true } +consensus_authorities = { package = "substrate-consensus-authorities", path = "../../core/consensus/authorities", default-features = false } [features] default = ["std"] diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index d720681ebe..8be70a6bcf 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -34,7 +34,8 @@ use client::{ use runtime_primitives::{ApplyResult, generic, create_runtime_str}; use runtime_primitives::transaction_validity::TransactionValidity; use runtime_primitives::traits::{ - BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup, CurrencyToVoteHandler + BlakeTwo256, Block as BlockT, DigestFor, NumberFor, StaticLookup, CurrencyToVoteHandler, + AuthorityIdFor, }; use version::RuntimeVersion; use council::{motions as council_motions, voting as council_voting}; @@ -58,8 +59,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node"), impl_name: create_runtime_str!("substrate-node"), authoring_version: 10, - spec_version: 52, - impl_version: 52, + spec_version: 53, + impl_version: 53, apis: RUNTIME_API_VERSIONS, }; @@ -243,10 +244,6 @@ impl_runtime_apis! { VERSION } - fn authorities() -> Vec { - Consensus::authorities() - } - fn execute_block(block: Block) { Executive::execute_block(block) } @@ -335,4 +332,10 @@ impl_runtime_apis! { Aura::slot_duration() } } + + impl consensus_authorities::AuthoritiesApi for Runtime { + fn authorities() -> Vec> { + Consensus::authorities() + } + } } diff --git a/substrate/node/runtime/wasm/Cargo.lock b/substrate/node/runtime/wasm/Cargo.lock index 146474068a..d9bb349bc9 100644 --- a/substrate/node/runtime/wasm/Cargo.lock +++ b/substrate/node/runtime/wasm/Cargo.lock @@ -1296,6 +1296,7 @@ dependencies = [ "srml-treasury 0.1.0", "substrate-client 0.1.0", "substrate-consensus-aura-primitives 0.1.0", + "substrate-consensus-authorities 0.1.0", "substrate-keyring 0.1.0", "substrate-offchain-primitives 0.1.0", "substrate-primitives 0.1.0", @@ -2593,6 +2594,20 @@ dependencies = [ "substrate-client 0.1.0", ] +[[package]] +name = "substrate-consensus-authorities" +version = "0.1.0" +dependencies = [ + "parity-codec 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 0.1.0", + "sr-primitives 0.1.0", + "sr-std 0.1.0", + "sr-version 0.1.0", + "srml-support 0.1.0", + "substrate-client 0.1.0", + "substrate-primitives 0.1.0", +] + [[package]] name = "substrate-consensus-common" version = "0.1.0" diff --git a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm index 1ae0a286ed67cacff0b234677c5dd9ac481859bc..53c5a9e15e62c336bdc60a099e749cd65657a04e 100644 GIT binary patch delta 71424 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 z z9ABHtlIqEP)1xqOMi()$Fbol*glG)$4-ukRZope z`#%Qz5_TwE2Gc2;0iRi2T3tt_-T5t4YQjCjg21uD!rHpLtop3DZmV@{biu^*E$_r^2{w$&Z{)^#|dprEgcon|BSjv#k3)TSG8y=H#7w`T!WQ__Z7$;f|-HLD#+Pb^?NU9`9r<-kX;!6zTBJM0Jxcqfb z_4E?oz4163ok!T!lPDa9^8(x~RPN>bt!KSzabgD#1~>6=0&n5KdU?DA1xPCuVHQQh z0SCe~=8Eb=8m@?RFUYtE09}viKmZL9hWo0MQ8Ep|1Wmz#2W~&XF0k*f9>OVr-jcYp8fxm5Jy$W=^ zi6E;RA|8ZLpRyX=>-r>2nM0m{2aXsC4qf4~Ik<_0!^?-OM^I_suHLk%(`QFPc@jtn z<-$wAPQsDJBh{trGtWN3y0ycVE(kFwFq~o|NFlAMZn&5PYeE4>BT|OFgh-sf7F#K9 zdJ|5jXa#ZcM29hmD6ywMT74AXG!Js+l6kC_-?#~9+z8c#uofX(91HS6EmoKBb>%hO z1oLp2MzVYb9P2*Ddu(Qh48V4c~Iv=Y{Mm% zz!HWAO&_S5{y5eiz1+WJI0Khlfq-*R#Ia2Z1c7^XXFc!yBz7R~oWa9%;XC582dugc zs24S~W*F<<-ZvZxr(hO26gZmGH&!1w3X@N!n-v$I_%c!Mmh|g8` zj;+e*R_odDA!uR{Tcv0T{Bc|c#Fl@)x@3FK;O*wI_IX@a#kq~H8*o%{mBHA;zkZrY z1r67caEb#z0b2v^-3!$N__y;|$F?8|9RVBOH7)3E+|LXD5O2^NyjMCjM7R@o@C-NP z6P{uFA@ZmAIs&A)j|dwQcd~AM2D%alHefsx>;(^6mE4!P^9^ zfw2rR1tt#p;m@(UT;Sti7_MrFBM$`L1OCEuOk>y`;7%Jef*Y+quX&yw-?rfwe|sRV NZ0{$j`zc!g{|CBF(|rH{ delta 70910 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 Module { // NOTE: this must be the last potential bailer, since it changes state. T::Currency::reserve(&who, Self::voting_bond())?; - >::mutate(|mut v| v.push(who.clone())); + >::mutate(|v| v.push(who.clone())); } >::insert(&who, index); >::insert(&who, votes);