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
This commit is contained in:
Stanislav Tkach
2019-03-29 18:41:22 +02:00
committed by Gav Wood
parent 55788fdf77
commit cbfc36b39f
44 changed files with 650 additions and 409 deletions
+123 -47
View File
@@ -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<T> CacheItemT for T where T: Clone + Decode + Encode + PartialEq {}
/// Database-backed blockchain data cache.
pub struct DbCache<Block: BlockT> {
authorities_at: ListCache<Block, Vec<AuthorityIdFor<Block>>, self::list_storage::DbStorage>,
cache_at: HashMap<Vec<u8>, ListCache<Block, Vec<u8>, self::list_storage::DbStorage>>,
db: Arc<KeyValueDB>,
key_lookup_column: Option<u32>,
header_column: Option<u32>,
authorities_column: Option<u32>,
best_finalized_block: ComplexBlockId<Block>,
}
impl<Block: BlockT> DbCache<Block> {
@@ -76,19 +81,13 @@ impl<Block: BlockT> DbCache<Block> {
authorities_column: Option<u32>,
best_finalized_block: ComplexBlockId<Block>,
) -> 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<Block: BlockT> DbCache<Block> {
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<Block>) {
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<u8>) -> &mut ListCache<Block, Vec<u8>, 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<Vec<u8>, ListCache<Block, Vec<u8>, self::list_storage::DbStorage>>,
name: Vec<u8>,
db: &Arc<KeyValueDB>,
key_lookup: Option<u32>,
header: Option<u32>,
cache: Option<u32>,
best_finalized_block: &ComplexBlockId<Block>,
) -> &'a mut ListCache<Block, Vec<u8>, 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<Block: BlockT> {
authorities_at_op: Option<self::list_cache::CommitOperation<Block, Vec<AuthorityIdFor<Block>>>>,
cache_at_op: HashMap<Vec<u8>, self::list_cache::CommitOperation<Block, Vec<u8>>>,
best_finalized_block: Option<ComplexBlockId<Block>>,
}
/// Database-backed blockchain data cache transaction valid for single block import.
pub struct DbCacheTransaction<'a, Block: BlockT> {
cache: &'a mut DbCache<Block>,
tx: &'a mut DBTransaction,
authorities_at_op: Option<self::list_cache::CommitOperation<Block, Vec<AuthorityIdFor<Block>>>>,
cache_at_op: HashMap<Vec<u8>, self::list_cache::CommitOperation<Block, Vec<u8>>>,
best_finalized_block: Option<ComplexBlockId<Block>>,
}
impl<'a, Block: BlockT> DbCacheTransaction<'a, Block> {
/// Convert transaction into post-commit operations set.
pub fn into_ops(self) -> DbCacheTransactionOps<Block> {
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>,
block: ComplexBlockId<Block>,
authorities_at: Option<Vec<AuthorityIdFor<Block>>>,
data_at: HashMap<Vec<u8>, Vec<u8>>,
is_final: bool,
) -> ClientResult<Self> {
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::<Vec<_>>();
let mut insert_op = |name: Vec<u8>, value: Option<Vec<u8>>| -> 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>,
block: ComplexBlockId<Block>
) -> ClientResult<Self> {
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<Block: BlockT>(pub RwLock<DbCache<Block>>);
impl<Block: BlockT> BlockchainCache<Block> for DbCacheSync<Block> {
fn authorities_at(&self, at: BlockId<Block>) -> Option<Vec<AuthorityIdFor<Block>>> {
fn get_at(&self, key: &[u8], at: &BlockId<Block>) -> Option<Vec<u8>> {
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::<Block>(
&**db,
@@ -202,6 +277,7 @@ impl<Block: BlockT> BlockchainCache<Block> for DbCacheSync<Block> {
},
};
cache.authorities_at.value_at_block(&at).ok()?
cache.cache_at.get(key)?.value_at_block(&at).ok()?
}
}
+11 -4
View File
@@ -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<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> {
Ok(self.meta.read().finalized_hash.clone())
}
fn cache(&self) -> Option<&client::blockchain::Cache<Block>> {
fn cache(&self) -> Option<Arc<client::blockchain::Cache<Block>>> {
None
}
@@ -256,6 +257,12 @@ impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> {
}
}
impl<Block: BlockT> client::blockchain::ProvideCache<Block> for BlockchainDb<Block> {
fn cache(&self) -> Option<Arc<client::blockchain::Cache<Block>>> {
None
}
}
/// Database transaction
pub struct BlockImportOperation<Block: BlockT, H: Hasher> {
old_state: CachingState<Blake2Hasher, DbState, Block>,
@@ -306,8 +313,8 @@ where Block: BlockT<Hash=H256>,
Ok(())
}
fn update_authorities(&mut self, _authorities: Vec<AuthorityIdFor<Block>>) {
// currently authorities are not cached on full nodes
fn update_cache(&mut self, _cache: HashMap<Vec<u8>, Vec<u8>>) {
// Currently cache isn't implemented on full nodes.
}
fn update_db_storage(&mut self, update: PrefixedMemoryDB<Blake2Hasher>) -> Result<(), client::error::Error> {
+156 -110
View File
@@ -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<Block: BlockT> {
db: Arc<KeyValueDB>,
meta: RwLock<Meta<NumberFor<Block>, Block::Hash>>,
leaves: RwLock<LeafSet<Block::Hash, NumberFor<Block>>>,
cache: DbCacheSync<Block>,
cache: Arc<DbCacheSync<Block>>,
}
impl<Block> LightStorage<Block>
@@ -96,7 +96,7 @@ impl<Block> LightStorage<Block>
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<Block> LightBlockchainStorage<Block> for LightStorage<Block>
fn import_header(
&self,
header: Block::Header,
authorities: Option<Vec<AuthorityIdFor<Block>>>,
cache_at: HashMap<Vec<u8>, Vec<u8>>,
leaf_state: NewBlockState,
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
) -> ClientResult<()> {
@@ -432,7 +432,7 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block>
.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<Block> LightBlockchainStorage<Block> for LightStorage<Block>
Ok(self.meta.read().finalized_hash.clone())
}
fn cache(&self) -> Option<&BlockchainCache<Block>> {
Some(&self.cache)
fn cache(&self) -> Option<Arc<BlockchainCache<Block>>> {
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<ExtrinsicWrapper<u32>>;
@@ -567,41 +569,41 @@ pub(crate) mod tests {
pub fn insert_block<F: Fn() -> Header>(
db: &LightStorage<Block>,
authorities: Option<Vec<AuthorityIdFor<Block>>>,
cache: HashMap<Vec<u8>, Vec<u8>>,
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<F: Fn() -> Header>(
db: &LightStorage<Block>,
authorities: Option<Vec<AuthorityIdFor<Block>>>,
cache: HashMap<Vec<u8>, Vec<u8>>,
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<F: Fn() -> Header>(
db: &LightStorage<Block>,
authorities: Option<Vec<AuthorityIdFor<Block>>>,
cache: HashMap<Vec<u8>, Vec<u8>>,
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<Block>, max: u64, checks: &[(u64, Option<Vec<AuthorityIdFor<Block>>>)]) {
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<u8>, Vec<u8>> {
HashMap::new()
}
fn make_authorities(authorities: Vec<AuthorityId>) -> HashMap<Vec<u8>, Vec<u8>> {
let mut map = HashMap::new();
map.insert(well_known_cache_keys::AUTHORITIES.to_vec(), authorities.encode());
map
}
fn get_authorities(cache: &BlockchainCache<Block>, at: BlockId<Block>) -> Option<Vec<AuthorityId>> {
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::<Block>::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]);