mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 07:41:08 +00:00
Run cargo fmt on the whole code base (#9394)
* Run cargo fmt on the whole code base * Second run * Add CI check * Fix compilation * More unnecessary braces * Handle weights * Use --all * Use correct attributes... * Fix UI tests * AHHHHHHHHH * 🤦 * Docs * Fix compilation * 🤷 * Please stop * 🤦 x 2 * More * make rustfmt.toml consistent with polkadot Co-authored-by: André Silva <andrerfosilva@gmail.com>
This commit is contained in:
@@ -18,27 +18,31 @@
|
||||
|
||||
//! State backend that's useful for benchmarking
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
collections::HashMap,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use hash_db::{Prefix, Hasher};
|
||||
use sp_trie::{MemoryDB, prefixed_key};
|
||||
use crate::storage_cache::{new_shared_cache, CachingState, SharedCache};
|
||||
use hash_db::{Hasher, Prefix};
|
||||
use kvdb::{DBTransaction, KeyValueDB};
|
||||
use sp_core::{
|
||||
hexdisplay::HexDisplay,
|
||||
storage::{ChildInfo, TrackedStorageKey},
|
||||
hexdisplay::HexDisplay
|
||||
};
|
||||
use sp_runtime::traits::{Block as BlockT, HashFor};
|
||||
use sp_runtime::Storage;
|
||||
use sp_runtime::{
|
||||
traits::{Block as BlockT, HashFor},
|
||||
Storage,
|
||||
};
|
||||
use sp_state_machine::{
|
||||
DBValue, backend::Backend as StateBackend, StorageCollection, ChildStorageCollection, ProofRecorder,
|
||||
backend::Backend as StateBackend, ChildStorageCollection, DBValue, ProofRecorder,
|
||||
StorageCollection,
|
||||
};
|
||||
use kvdb::{KeyValueDB, DBTransaction};
|
||||
use crate::storage_cache::{CachingState, SharedCache, new_shared_cache};
|
||||
use sp_trie::{prefixed_key, MemoryDB};
|
||||
|
||||
type DbState<B> = sp_state_machine::TrieBackend<
|
||||
Arc<dyn sp_state_machine::Storage<HashFor<B>>>, HashFor<B>
|
||||
>;
|
||||
type DbState<B> =
|
||||
sp_state_machine::TrieBackend<Arc<dyn sp_state_machine::Storage<HashFor<B>>>, HashFor<B>>;
|
||||
|
||||
type State<B> = CachingState<DbState<B>, B>;
|
||||
|
||||
@@ -53,14 +57,17 @@ impl<Block: BlockT> sp_state_machine::Storage<HashFor<Block>> for StorageDb<Bloc
|
||||
let prefixed_key = prefixed_key::<HashFor<Block>>(key, prefix);
|
||||
if let Some(recorder) = &self.proof_recorder {
|
||||
if let Some(v) = recorder.get(&key) {
|
||||
return Ok(v.clone());
|
||||
return Ok(v.clone())
|
||||
}
|
||||
let backend_value = self.db.get(0, &prefixed_key)
|
||||
let backend_value = self
|
||||
.db
|
||||
.get(0, &prefixed_key)
|
||||
.map_err(|e| format!("Database backend error: {:?}", e))?;
|
||||
recorder.record(key.clone(), backend_value.clone());
|
||||
Ok(backend_value)
|
||||
} else {
|
||||
self.db.get(0, &prefixed_key)
|
||||
self.db
|
||||
.get(0, &prefixed_key)
|
||||
.map_err(|e| format!("Database backend error: {:?}", e))
|
||||
}
|
||||
}
|
||||
@@ -91,7 +98,11 @@ pub struct BenchmarkingState<B: BlockT> {
|
||||
|
||||
impl<B: BlockT> BenchmarkingState<B> {
|
||||
/// Create a new instance that creates a database in a temporary dir.
|
||||
pub fn new(genesis: Storage, _cache_size_mb: Option<usize>, record_proof: bool) -> Result<Self, String> {
|
||||
pub fn new(
|
||||
genesis: Storage,
|
||||
_cache_size_mb: Option<usize>,
|
||||
record_proof: bool,
|
||||
) -> Result<Self, String> {
|
||||
let mut root = B::Hash::default();
|
||||
let mut mdb = MemoryDB::<HashFor<B>>::default();
|
||||
sp_state_machine::TrieDBMut::<HashFor<B>>::new(&mut mdb, &mut root);
|
||||
@@ -114,14 +125,17 @@ impl<B: BlockT> BenchmarkingState<B> {
|
||||
state.add_whitelist_to_tracker();
|
||||
|
||||
state.reopen()?;
|
||||
let child_delta = genesis.children_default.iter().map(|(_storage_key, child_content)| (
|
||||
&child_content.child_info,
|
||||
child_content.data.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))),
|
||||
));
|
||||
let (root, transaction): (B::Hash, _) = state.state.borrow_mut().as_mut().unwrap().full_storage_root(
|
||||
genesis.top.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))),
|
||||
child_delta,
|
||||
);
|
||||
let child_delta = genesis.children_default.iter().map(|(_storage_key, child_content)| {
|
||||
(
|
||||
&child_content.child_info,
|
||||
child_content.data.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))),
|
||||
)
|
||||
});
|
||||
let (root, transaction): (B::Hash, _) =
|
||||
state.state.borrow_mut().as_mut().unwrap().full_storage_root(
|
||||
genesis.top.iter().map(|(k, v)| (k.as_ref(), Some(v.as_ref()))),
|
||||
child_delta,
|
||||
);
|
||||
state.genesis = transaction.clone().drain();
|
||||
state.genesis_root = root.clone();
|
||||
state.commit(root, transaction, Vec::new(), Vec::new())?;
|
||||
@@ -143,12 +157,12 @@ impl<B: BlockT> BenchmarkingState<B> {
|
||||
let storage_db = Arc::new(StorageDb::<B> {
|
||||
db,
|
||||
proof_recorder: self.proof_recorder.clone(),
|
||||
_block: Default::default()
|
||||
_block: Default::default(),
|
||||
});
|
||||
*self.state.borrow_mut() = Some(State::new(
|
||||
DbState::<B>::new(storage_db, self.root.get()),
|
||||
self.shared_cache.clone(),
|
||||
None
|
||||
None,
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
@@ -178,7 +192,7 @@ impl<B: BlockT> BenchmarkingState<B> {
|
||||
|
||||
let key_tracker = if let Some(childtrie) = childtrie {
|
||||
child_key_tracker.entry(childtrie.to_vec()).or_insert_with(|| HashMap::new())
|
||||
} else {
|
||||
} else {
|
||||
&mut main_key_tracker
|
||||
};
|
||||
|
||||
@@ -193,7 +207,7 @@ impl<B: BlockT> BenchmarkingState<B> {
|
||||
let should_log = !tracker.has_been_read();
|
||||
tracker.add_read();
|
||||
should_log
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if should_log {
|
||||
@@ -215,7 +229,7 @@ impl<B: BlockT> BenchmarkingState<B> {
|
||||
|
||||
let key_tracker = if let Some(childtrie) = childtrie {
|
||||
child_key_tracker.entry(childtrie.to_vec()).or_insert_with(|| HashMap::new())
|
||||
} else {
|
||||
} else {
|
||||
&mut main_key_tracker
|
||||
};
|
||||
|
||||
@@ -231,7 +245,7 @@ impl<B: BlockT> BenchmarkingState<B> {
|
||||
let should_log = !tracker.has_been_written();
|
||||
tracker.add_write();
|
||||
should_log
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if should_log {
|
||||
@@ -269,7 +283,7 @@ fn state_err() -> String {
|
||||
}
|
||||
|
||||
impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
|
||||
type Error = <DbState<B> as StateBackend<HashFor<B>>>::Error;
|
||||
type Error = <DbState<B> as StateBackend<HashFor<B>>>::Error;
|
||||
type Transaction = <DbState<B> as StateBackend<HashFor<B>>>::Transaction;
|
||||
type TrieBackendStorage = <DbState<B> as StateBackend<HashFor<B>>>::TrieBackendStorage;
|
||||
|
||||
@@ -289,7 +303,11 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
|
||||
key: &[u8],
|
||||
) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
self.add_read_key(Some(child_info.storage_key()), key);
|
||||
self.state.borrow().as_ref().ok_or_else(state_err)?.child_storage(child_info, key)
|
||||
self.state
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.ok_or_else(state_err)?
|
||||
.child_storage(child_info, key)
|
||||
}
|
||||
|
||||
fn exists_storage(&self, key: &[u8]) -> Result<bool, Self::Error> {
|
||||
@@ -303,7 +321,11 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
|
||||
key: &[u8],
|
||||
) -> Result<bool, Self::Error> {
|
||||
self.add_read_key(Some(child_info.storage_key()), key);
|
||||
self.state.borrow().as_ref().ok_or_else(state_err)?.exists_child_storage(child_info, key)
|
||||
self.state
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.ok_or_else(state_err)?
|
||||
.exists_child_storage(child_info, key)
|
||||
}
|
||||
|
||||
fn next_storage_key(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
@@ -317,7 +339,11 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
|
||||
key: &[u8],
|
||||
) -> Result<Option<Vec<u8>>, Self::Error> {
|
||||
self.add_read_key(Some(child_info.storage_key()), key);
|
||||
self.state.borrow().as_ref().ok_or_else(state_err)?.next_child_storage_key(child_info, key)
|
||||
self.state
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.ok_or_else(state_err)?
|
||||
.next_child_storage_key(child_info, key)
|
||||
}
|
||||
|
||||
fn for_keys_with_prefix<F: FnMut(&[u8])>(&self, prefix: &[u8], f: F) {
|
||||
@@ -340,8 +366,13 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
|
||||
f: F,
|
||||
allow_missing: bool,
|
||||
) -> Result<bool, Self::Error> {
|
||||
self.state.borrow().as_ref().ok_or_else(state_err)?
|
||||
.apply_to_key_values_while(child_info, prefix, start_at, f, allow_missing)
|
||||
self.state.borrow().as_ref().ok_or_else(state_err)?.apply_to_key_values_while(
|
||||
child_info,
|
||||
prefix,
|
||||
start_at,
|
||||
f,
|
||||
allow_missing,
|
||||
)
|
||||
}
|
||||
|
||||
fn apply_to_keys_while<F: FnMut(&[u8]) -> bool>(
|
||||
@@ -368,17 +399,29 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
|
||||
|
||||
fn storage_root<'a>(
|
||||
&self,
|
||||
delta: impl Iterator<Item=(&'a [u8], Option<&'a [u8]>)>,
|
||||
) -> (B::Hash, Self::Transaction) where B::Hash: Ord {
|
||||
self.state.borrow().as_ref().map_or(Default::default(), |s| s.storage_root(delta))
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
) -> (B::Hash, Self::Transaction)
|
||||
where
|
||||
B::Hash: Ord,
|
||||
{
|
||||
self.state
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map_or(Default::default(), |s| s.storage_root(delta))
|
||||
}
|
||||
|
||||
fn child_storage_root<'a>(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
delta: impl Iterator<Item=(&'a [u8], Option<&'a [u8]>)>,
|
||||
) -> (B::Hash, bool, Self::Transaction) where B::Hash: Ord {
|
||||
self.state.borrow().as_ref().map_or(Default::default(), |s| s.child_storage_root(child_info, delta))
|
||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
||||
) -> (B::Hash, bool, Self::Transaction)
|
||||
where
|
||||
B::Hash: Ord,
|
||||
{
|
||||
self.state
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map_or(Default::default(), |s| s.child_storage_root(child_info, delta))
|
||||
}
|
||||
|
||||
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
@@ -389,17 +432,16 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
|
||||
self.state.borrow().as_ref().map_or(Default::default(), |s| s.keys(prefix))
|
||||
}
|
||||
|
||||
fn child_keys(
|
||||
&self,
|
||||
child_info: &ChildInfo,
|
||||
prefix: &[u8],
|
||||
) -> Vec<Vec<u8>> {
|
||||
self.state.borrow().as_ref().map_or(Default::default(), |s| s.child_keys(child_info, prefix))
|
||||
fn child_keys(&self, child_info: &ChildInfo, prefix: &[u8]) -> Vec<Vec<u8>> {
|
||||
self.state
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map_or(Default::default(), |s| s.child_keys(child_info, prefix))
|
||||
}
|
||||
|
||||
fn as_trie_backend(&mut self)
|
||||
-> Option<&sp_state_machine::TrieBackend<Self::TrieBackendStorage, HashFor<B>>>
|
||||
{
|
||||
fn as_trie_backend(
|
||||
&mut self,
|
||||
) -> Option<&sp_state_machine::TrieBackend<Self::TrieBackendStorage, HashFor<B>>> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -425,7 +467,8 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
|
||||
let mut record = self.record.take();
|
||||
record.extend(keys);
|
||||
self.record.set(record);
|
||||
db.write(db_transaction).map_err(|_| String::from("Error committing transaction"))?;
|
||||
db.write(db_transaction)
|
||||
.map_err(|_| String::from("Error committing transaction"))?;
|
||||
self.root.set(storage_root);
|
||||
self.db.set(Some(db));
|
||||
|
||||
@@ -455,7 +498,8 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
|
||||
None => db_transaction.delete(0, &key),
|
||||
}
|
||||
}
|
||||
db.write(db_transaction).map_err(|_| String::from("Error committing transaction"))?;
|
||||
db.write(db_transaction)
|
||||
.map_err(|_| String::from("Error committing transaction"))?;
|
||||
self.db.set(Some(db));
|
||||
}
|
||||
|
||||
@@ -519,24 +563,20 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
|
||||
let reads = tracker.reads.min(1);
|
||||
let writes = tracker.writes.min(1);
|
||||
if let Some(prefix_tracker) = prefix_key_tracker.get_mut(&prefix) {
|
||||
prefix_tracker.0 += reads;
|
||||
prefix_tracker.1 += writes;
|
||||
prefix_tracker.0 += reads;
|
||||
prefix_tracker.1 += writes;
|
||||
} else {
|
||||
prefix_key_tracker.insert(
|
||||
prefix,
|
||||
(
|
||||
reads,
|
||||
writes,
|
||||
tracker.whitelisted,
|
||||
),
|
||||
);
|
||||
prefix_key_tracker.insert(prefix, (reads, writes, tracker.whitelisted));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
prefix_key_tracker.iter().map(|(key, tracker)| -> (Vec<u8>, u32, u32, bool) {
|
||||
prefix_key_tracker
|
||||
.iter()
|
||||
.map(|(key, tracker)| -> (Vec<u8>, u32, u32, bool) {
|
||||
(key.to_vec(), tracker.0, tracker.1, tracker.2)
|
||||
}).collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn register_overlay_stats(&self, stats: &sp_state_machine::StateMachineStats) {
|
||||
@@ -544,7 +584,10 @@ impl<B: BlockT> StateBackend<HashFor<B>> for BenchmarkingState<B> {
|
||||
}
|
||||
|
||||
fn usage_info(&self) -> sp_state_machine::UsageInfo {
|
||||
self.state.borrow().as_ref().map_or(sp_state_machine::UsageInfo::empty(), |s| s.usage_info())
|
||||
self.state
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.map_or(sp_state_machine::UsageInfo::empty(), |s| s.usage_info())
|
||||
}
|
||||
|
||||
fn proof_size(&self) -> Option<u32> {
|
||||
@@ -585,8 +628,8 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn read_to_main_and_child_tries() {
|
||||
let bench_state = BenchmarkingState::<crate::tests::Block>::new(Default::default(), None, false)
|
||||
.unwrap();
|
||||
let bench_state =
|
||||
BenchmarkingState::<crate::tests::Block>::new(Default::default(), None, false).unwrap();
|
||||
|
||||
for _ in 0..2 {
|
||||
let child1 = sp_core::storage::ChildInfo::new_default(b"child1");
|
||||
@@ -600,16 +643,14 @@ mod test {
|
||||
bench_state.child_storage(&child1, b"bar").unwrap();
|
||||
bench_state.child_storage(&child2, b"bar").unwrap();
|
||||
|
||||
bench_state.commit(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
vec![
|
||||
("foo".as_bytes().to_vec(), None)
|
||||
],
|
||||
vec![
|
||||
("child1".as_bytes().to_vec(), vec![("foo".as_bytes().to_vec(), None)])
|
||||
]
|
||||
).unwrap();
|
||||
bench_state
|
||||
.commit(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
vec![("foo".as_bytes().to_vec(), None)],
|
||||
vec![("child1".as_bytes().to_vec(), vec![("foo".as_bytes().to_vec(), None)])],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let rw_tracker = bench_state.read_write_count();
|
||||
assert_eq!(rw_tracker.0, 6);
|
||||
|
||||
+1026
-457
File diff suppressed because it is too large
Load Diff
+54
-34
@@ -18,12 +18,11 @@
|
||||
|
||||
//! List-cache storage entries.
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use sp_blockchain::Result as ClientResult;
|
||||
use sp_runtime::traits::{Block as BlockT, NumberFor};
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
use crate::cache::{CacheItemT, ComplexBlockId};
|
||||
use crate::cache::list_storage::{Storage};
|
||||
use crate::cache::{list_storage::Storage, CacheItemT, ComplexBlockId};
|
||||
|
||||
/// Single list-based cache entry.
|
||||
#[derive(Debug)]
|
||||
@@ -52,10 +51,8 @@ impl<Block: BlockT, T: CacheItemT> Entry<Block, T> {
|
||||
match value {
|
||||
Some(value) => match self.value == value {
|
||||
true => None,
|
||||
false => Some(StorageEntry {
|
||||
prev_valid_from: Some(self.valid_from.clone()),
|
||||
value,
|
||||
}),
|
||||
false =>
|
||||
Some(StorageEntry { prev_valid_from: Some(self.valid_from.clone()), value }),
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
@@ -67,7 +64,8 @@ impl<Block: BlockT, T: CacheItemT> Entry<Block, T> {
|
||||
storage: &S,
|
||||
block: NumberFor<Block>,
|
||||
) -> ClientResult<Option<(ComplexBlockId<Block>, Option<ComplexBlockId<Block>>)>> {
|
||||
Ok(self.search_best_before(storage, block)?
|
||||
Ok(self
|
||||
.search_best_before(storage, block)?
|
||||
.map(|(entry, next)| (entry.valid_from, next)))
|
||||
}
|
||||
|
||||
@@ -86,14 +84,14 @@ impl<Block: BlockT, T: CacheItemT> Entry<Block, T> {
|
||||
let mut current = self.valid_from.clone();
|
||||
if block >= self.valid_from.number {
|
||||
let value = self.value.clone();
|
||||
return Ok(Some((Entry { valid_from: current, value }, next)));
|
||||
return Ok(Some((Entry { valid_from: current, value }, next)))
|
||||
}
|
||||
|
||||
// else - travel back in time
|
||||
loop {
|
||||
let entry = storage.require_entry(¤t)?;
|
||||
if block >= current.number {
|
||||
return Ok(Some((Entry { valid_from: current, value: entry.value }, next)));
|
||||
return Ok(Some((Entry { valid_from: current, value: entry.value }, next)))
|
||||
}
|
||||
|
||||
next = Some(current);
|
||||
@@ -108,18 +106,15 @@ impl<Block: BlockT, T: CacheItemT> Entry<Block, T> {
|
||||
impl<Block: BlockT, T: CacheItemT> StorageEntry<Block, T> {
|
||||
/// Converts storage entry into an entry, valid from given block.
|
||||
pub fn into_entry(self, valid_from: ComplexBlockId<Block>) -> Entry<Block, T> {
|
||||
Entry {
|
||||
valid_from,
|
||||
value: self.value,
|
||||
}
|
||||
Entry { valid_from, value: self.value }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::cache::list_storage::tests::{DummyStorage, FaultyStorage};
|
||||
use substrate_test_runtime_client::runtime::{H256, Block};
|
||||
use super::*;
|
||||
use crate::cache::list_storage::tests::{DummyStorage, FaultyStorage};
|
||||
use substrate_test_runtime_client::runtime::{Block, H256};
|
||||
|
||||
fn test_id(number: u64) -> ComplexBlockId<Block> {
|
||||
ComplexBlockId::new(H256::from_low_u64_be(number), number)
|
||||
@@ -132,36 +127,61 @@ mod tests {
|
||||
// when trying to update with the same Some value
|
||||
assert_eq!(Entry { valid_from: test_id(1), value: 1 }.try_update(Some(1)), None);
|
||||
// when trying to update with different Some value
|
||||
assert_eq!(Entry { valid_from: test_id(1), value: 1 }.try_update(Some(2)),
|
||||
Some(StorageEntry { prev_valid_from: Some(test_id(1)), value: 2 }));
|
||||
assert_eq!(
|
||||
Entry { valid_from: test_id(1), value: 1 }.try_update(Some(2)),
|
||||
Some(StorageEntry { prev_valid_from: Some(test_id(1)), value: 2 })
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entry_search_best_before_fails() {
|
||||
// when storage returns error
|
||||
assert!(Entry::<_, u64> { valid_from: test_id(100), value: 42 }
|
||||
.search_best_before(&FaultyStorage, 50).is_err());
|
||||
.search_best_before(&FaultyStorage, 50)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entry_search_best_before_works() {
|
||||
// when block is better than our best block
|
||||
assert_eq!(Entry::<_, u64> { valid_from: test_id(100), value: 100 }
|
||||
.search_best_before(&DummyStorage::new(), 150).unwrap(),
|
||||
Some((Entry::<_, u64> { valid_from: test_id(100), value: 100 }, None)));
|
||||
assert_eq!(
|
||||
Entry::<_, u64> { valid_from: test_id(100), value: 100 }
|
||||
.search_best_before(&DummyStorage::new(), 150)
|
||||
.unwrap(),
|
||||
Some((Entry::<_, u64> { valid_from: test_id(100), value: 100 }, None))
|
||||
);
|
||||
// when block is found between two entries
|
||||
assert_eq!(Entry::<_, u64> { valid_from: test_id(100), value: 100 }
|
||||
.search_best_before(&DummyStorage::new()
|
||||
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 })
|
||||
.with_entry(test_id(50), StorageEntry { prev_valid_from: Some(test_id(30)), value: 50 }),
|
||||
75).unwrap(),
|
||||
Some((Entry::<_, u64> { valid_from: test_id(50), value: 50 }, Some(test_id(100)))));
|
||||
assert_eq!(
|
||||
Entry::<_, u64> { valid_from: test_id(100), value: 100 }
|
||||
.search_best_before(
|
||||
&DummyStorage::new()
|
||||
.with_entry(
|
||||
test_id(100),
|
||||
StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 }
|
||||
)
|
||||
.with_entry(
|
||||
test_id(50),
|
||||
StorageEntry { prev_valid_from: Some(test_id(30)), value: 50 }
|
||||
),
|
||||
75
|
||||
)
|
||||
.unwrap(),
|
||||
Some((Entry::<_, u64> { valid_from: test_id(50), value: 50 }, Some(test_id(100))))
|
||||
);
|
||||
// when block is not found
|
||||
assert_eq!(Entry::<_, u64> { valid_from: test_id(100), value: 100 }
|
||||
.search_best_before(&DummyStorage::new()
|
||||
.with_entry(test_id(100), StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 })
|
||||
.with_entry(test_id(50), StorageEntry { prev_valid_from: None, value: 50 }),
|
||||
30).unwrap(),
|
||||
None);
|
||||
assert_eq!(
|
||||
Entry::<_, u64> { valid_from: test_id(100), value: 100 }
|
||||
.search_best_before(
|
||||
&DummyStorage::new()
|
||||
.with_entry(
|
||||
test_id(100),
|
||||
StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 }
|
||||
)
|
||||
.with_entry(test_id(50), StorageEntry { prev_valid_from: None, value: 50 }),
|
||||
30
|
||||
)
|
||||
.unwrap(),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+100
-45
@@ -20,17 +20,23 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use sp_blockchain::{Error as ClientError, Result as ClientResult};
|
||||
use codec::{Encode, Decode};
|
||||
use sp_runtime::generic::BlockId;
|
||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor};
|
||||
use sp_database::{Database, Transaction};
|
||||
use crate::utils::{self, meta_keys};
|
||||
use codec::{Decode, Encode};
|
||||
use sp_blockchain::{Error as ClientError, Result as ClientResult};
|
||||
use sp_database::{Database, Transaction};
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, Header as HeaderT, NumberFor},
|
||||
};
|
||||
|
||||
use crate::cache::{CacheItemT, ComplexBlockId};
|
||||
use crate::cache::list_cache::{CommitOperation, Fork};
|
||||
use crate::cache::list_entry::{Entry, StorageEntry};
|
||||
use crate::DbHash;
|
||||
use crate::{
|
||||
cache::{
|
||||
list_cache::{CommitOperation, Fork},
|
||||
list_entry::{Entry, StorageEntry},
|
||||
CacheItemT, ComplexBlockId,
|
||||
},
|
||||
DbHash,
|
||||
};
|
||||
|
||||
/// Single list-cache metadata.
|
||||
#[derive(Debug)]
|
||||
@@ -54,14 +60,21 @@ pub trait Storage<Block: BlockT, T: CacheItemT> {
|
||||
fn read_meta(&self) -> ClientResult<Metadata<Block>>;
|
||||
|
||||
/// Reads cache entry from the storage.
|
||||
fn read_entry(&self, at: &ComplexBlockId<Block>) -> ClientResult<Option<StorageEntry<Block, T>>>;
|
||||
fn read_entry(
|
||||
&self,
|
||||
at: &ComplexBlockId<Block>,
|
||||
) -> ClientResult<Option<StorageEntry<Block, T>>>;
|
||||
|
||||
/// Reads referenced (and thus existing) cache entry from the storage.
|
||||
fn require_entry(&self, at: &ComplexBlockId<Block>) -> ClientResult<StorageEntry<Block, T>> {
|
||||
self.read_entry(at)
|
||||
.and_then(|entry| entry
|
||||
.ok_or_else(|| ClientError::from(
|
||||
ClientError::Backend(format!("Referenced cache entry at {:?} is not found", at)))))
|
||||
self.read_entry(at).and_then(|entry| {
|
||||
entry.ok_or_else(|| {
|
||||
ClientError::from(ClientError::Backend(format!(
|
||||
"Referenced cache entry at {:?} is not found",
|
||||
at
|
||||
)))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,10 +124,14 @@ impl DbStorage {
|
||||
}
|
||||
|
||||
/// Get reference to the database.
|
||||
pub fn db(&self) -> &Arc<dyn Database<DbHash>> { &self.db }
|
||||
pub fn db(&self) -> &Arc<dyn Database<DbHash>> {
|
||||
&self.db
|
||||
}
|
||||
|
||||
/// Get reference to the database columns.
|
||||
pub fn columns(&self) -> &DbColumns { &self.columns }
|
||||
pub fn columns(&self) -> &DbColumns {
|
||||
&self.columns
|
||||
}
|
||||
|
||||
/// Encode block id for storing as a key in cache column.
|
||||
/// We append prefix to the actual encoding to allow several caches
|
||||
@@ -128,25 +145,35 @@ impl DbStorage {
|
||||
|
||||
impl<Block: BlockT, T: CacheItemT> Storage<Block, T> for DbStorage {
|
||||
fn read_id(&self, at: NumberFor<Block>) -> ClientResult<Option<Block::Hash>> {
|
||||
utils::read_header::<Block>(&*self.db, self.columns.key_lookup, self.columns.header, BlockId::Number(at))
|
||||
.map(|maybe_header| maybe_header.map(|header| header.hash()))
|
||||
utils::read_header::<Block>(
|
||||
&*self.db,
|
||||
self.columns.key_lookup,
|
||||
self.columns.header,
|
||||
BlockId::Number(at),
|
||||
)
|
||||
.map(|maybe_header| maybe_header.map(|header| header.hash()))
|
||||
}
|
||||
|
||||
fn read_header(&self, at: &Block::Hash) -> ClientResult<Option<Block::Header>> {
|
||||
utils::read_header::<Block>(&*self.db, self.columns.key_lookup, self.columns.header, BlockId::Hash(*at))
|
||||
utils::read_header::<Block>(
|
||||
&*self.db,
|
||||
self.columns.key_lookup,
|
||||
self.columns.header,
|
||||
BlockId::Hash(*at),
|
||||
)
|
||||
}
|
||||
|
||||
fn read_meta(&self) -> ClientResult<Metadata<Block>> {
|
||||
match self.db.get(self.columns.meta, &self.meta_key) {
|
||||
Some(meta) => meta::decode(&*meta),
|
||||
None => Ok(Metadata {
|
||||
finalized: None,
|
||||
unfinalized: Vec::new(),
|
||||
})
|
||||
None => Ok(Metadata { finalized: None, unfinalized: Vec::new() }),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_entry(&self, at: &ComplexBlockId<Block>) -> ClientResult<Option<StorageEntry<Block, T>>> {
|
||||
fn read_entry(
|
||||
&self,
|
||||
at: &ComplexBlockId<Block>,
|
||||
) -> ClientResult<Option<StorageEntry<Block, T>>> {
|
||||
match self.db.get(self.columns.cache, &self.encode_block_id(at)) {
|
||||
Some(entry) => StorageEntry::<Block, T>::decode(&mut &entry[..])
|
||||
.map_err(|_| ClientError::Backend("Failed to decode cache entry".into()))
|
||||
@@ -171,7 +198,11 @@ impl<'a> DbStorageTransaction<'a> {
|
||||
|
||||
impl<'a, Block: BlockT, T: CacheItemT> StorageTransaction<Block, T> for DbStorageTransaction<'a> {
|
||||
fn insert_storage_entry(&mut self, at: &ComplexBlockId<Block>, entry: &StorageEntry<Block, T>) {
|
||||
self.tx.set_from_vec(self.storage.columns.cache, &self.storage.encode_block_id(at), entry.encode());
|
||||
self.tx.set_from_vec(
|
||||
self.storage.columns.cache,
|
||||
&self.storage.encode_block_id(at),
|
||||
entry.encode(),
|
||||
);
|
||||
}
|
||||
|
||||
fn remove_storage_entry(&mut self, at: &ComplexBlockId<Block>) {
|
||||
@@ -187,7 +218,8 @@ impl<'a, Block: BlockT, T: CacheItemT> StorageTransaction<Block, T> for DbStorag
|
||||
self.tx.set_from_vec(
|
||||
self.storage.columns.meta,
|
||||
&self.storage.meta_key,
|
||||
meta::encode(best_finalized_entry, unfinalized, operation));
|
||||
meta::encode(best_finalized_entry, unfinalized, operation),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,10 +238,11 @@ mod meta {
|
||||
pub fn encode<Block: BlockT, T: CacheItemT>(
|
||||
best_finalized_entry: Option<&Entry<Block, T>>,
|
||||
unfinalized: &[Fork<Block, T>],
|
||||
op: &CommitOperation<Block, T>
|
||||
op: &CommitOperation<Block, T>,
|
||||
) -> Vec<u8> {
|
||||
let mut finalized = best_finalized_entry.as_ref().map(|entry| &entry.valid_from);
|
||||
let mut unfinalized = unfinalized.iter().map(|fork| &fork.head().valid_from).collect::<Vec<_>>();
|
||||
let mut unfinalized =
|
||||
unfinalized.iter().map(|fork| &fork.head().valid_from).collect::<Vec<_>>();
|
||||
|
||||
match op {
|
||||
CommitOperation::AppendNewBlock(_, _) => (),
|
||||
@@ -230,8 +263,11 @@ mod meta {
|
||||
CommitOperation::BlockReverted(ref forks) => {
|
||||
for (fork_index, updated_fork) in forks.iter().rev() {
|
||||
match updated_fork {
|
||||
Some(updated_fork) => unfinalized[*fork_index] = &updated_fork.head().valid_from,
|
||||
None => { unfinalized.remove(*fork_index); },
|
||||
Some(updated_fork) =>
|
||||
unfinalized[*fork_index] = &updated_fork.head().valid_from,
|
||||
None => {
|
||||
unfinalized.remove(*fork_index);
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -243,10 +279,12 @@ mod meta {
|
||||
/// Decode meta information.
|
||||
pub fn decode<Block: BlockT>(encoded: &[u8]) -> ClientResult<Metadata<Block>> {
|
||||
let input = &mut &*encoded;
|
||||
let finalized: Option<ComplexBlockId<Block>> = Decode::decode(input)
|
||||
.map_err(|_| ClientError::from(ClientError::Backend("Error decoding cache meta".into())))?;
|
||||
let unfinalized: Vec<ComplexBlockId<Block>> = Decode::decode(input)
|
||||
.map_err(|_| ClientError::from(ClientError::Backend("Error decoding cache meta".into())))?;
|
||||
let finalized: Option<ComplexBlockId<Block>> = Decode::decode(input).map_err(|_| {
|
||||
ClientError::from(ClientError::Backend("Error decoding cache meta".into()))
|
||||
})?;
|
||||
let unfinalized: Vec<ComplexBlockId<Block>> = Decode::decode(input).map_err(|_| {
|
||||
ClientError::from(ClientError::Backend("Error decoding cache meta".into()))
|
||||
})?;
|
||||
|
||||
Ok(Metadata { finalized, unfinalized })
|
||||
}
|
||||
@@ -254,8 +292,8 @@ mod meta {
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use super::*;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub struct FaultyStorage;
|
||||
|
||||
@@ -272,7 +310,10 @@ pub mod tests {
|
||||
Err(ClientError::Backend("TestError".into()))
|
||||
}
|
||||
|
||||
fn read_entry(&self, _at: &ComplexBlockId<Block>) -> ClientResult<Option<StorageEntry<Block, T>>> {
|
||||
fn read_entry(
|
||||
&self,
|
||||
_at: &ComplexBlockId<Block>,
|
||||
) -> ClientResult<Option<StorageEntry<Block, T>>> {
|
||||
Err(ClientError::Backend("TestError".into()))
|
||||
}
|
||||
}
|
||||
@@ -287,17 +328,18 @@ pub mod tests {
|
||||
impl<Block: BlockT, T: CacheItemT> DummyStorage<Block, T> {
|
||||
pub fn new() -> Self {
|
||||
DummyStorage {
|
||||
meta: Metadata {
|
||||
finalized: None,
|
||||
unfinalized: Vec::new(),
|
||||
},
|
||||
meta: Metadata { finalized: None, unfinalized: Vec::new() },
|
||||
ids: HashMap::new(),
|
||||
headers: HashMap::new(),
|
||||
entries: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_meta(mut self, finalized: Option<ComplexBlockId<Block>>, unfinalized: Vec<ComplexBlockId<Block>>) -> Self {
|
||||
pub fn with_meta(
|
||||
mut self,
|
||||
finalized: Option<ComplexBlockId<Block>>,
|
||||
unfinalized: Vec<ComplexBlockId<Block>>,
|
||||
) -> Self {
|
||||
self.meta.finalized = finalized;
|
||||
self.meta.unfinalized = unfinalized;
|
||||
self
|
||||
@@ -313,7 +355,11 @@ pub mod tests {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_entry(mut self, at: ComplexBlockId<Block>, entry: StorageEntry<Block, T>) -> Self {
|
||||
pub fn with_entry(
|
||||
mut self,
|
||||
at: ComplexBlockId<Block>,
|
||||
entry: StorageEntry<Block, T>,
|
||||
) -> Self {
|
||||
self.entries.insert(at.hash, entry);
|
||||
self
|
||||
}
|
||||
@@ -332,7 +378,10 @@ pub mod tests {
|
||||
Ok(self.meta.clone())
|
||||
}
|
||||
|
||||
fn read_entry(&self, at: &ComplexBlockId<Block>) -> ClientResult<Option<StorageEntry<Block, T>>> {
|
||||
fn read_entry(
|
||||
&self,
|
||||
at: &ComplexBlockId<Block>,
|
||||
) -> ClientResult<Option<StorageEntry<Block, T>>> {
|
||||
Ok(self.entries.get(&at.hash).cloned())
|
||||
}
|
||||
}
|
||||
@@ -366,7 +415,11 @@ pub mod tests {
|
||||
}
|
||||
|
||||
impl<Block: BlockT, T: CacheItemT> StorageTransaction<Block, T> for DummyTransaction<Block> {
|
||||
fn insert_storage_entry(&mut self, at: &ComplexBlockId<Block>, _entry: &StorageEntry<Block, T>) {
|
||||
fn insert_storage_entry(
|
||||
&mut self,
|
||||
at: &ComplexBlockId<Block>,
|
||||
_entry: &StorageEntry<Block, T>,
|
||||
) {
|
||||
self.inserted_entries.insert(at.hash);
|
||||
}
|
||||
|
||||
@@ -380,7 +433,9 @@ pub mod tests {
|
||||
unfinalized: &[Fork<Block, T>],
|
||||
operation: &CommitOperation<Block, T>,
|
||||
) {
|
||||
self.updated_meta = Some(meta::decode(&meta::encode(best_finalized_entry, unfinalized, operation)).unwrap());
|
||||
self.updated_meta = Some(
|
||||
meta::decode(&meta::encode(best_finalized_entry, unfinalized, operation)).unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+63
-59
@@ -18,17 +18,27 @@
|
||||
|
||||
//! DB-backed cache of blockchain data.
|
||||
|
||||
use std::{sync::Arc, collections::{HashMap, hash_map::Entry}};
|
||||
use parking_lot::RwLock;
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use sc_client_api::blockchain::{well_known_cache_keys::{self, Id as CacheKeyId}, Cache as BlockchainCache};
|
||||
use sp_blockchain::{Result as ClientResult, HeaderMetadataCache};
|
||||
use crate::{
|
||||
utils::{self, COLUMN_META},
|
||||
DbHash,
|
||||
};
|
||||
use codec::{Decode, Encode};
|
||||
use sc_client_api::blockchain::{
|
||||
well_known_cache_keys::{self, Id as CacheKeyId},
|
||||
Cache as BlockchainCache,
|
||||
};
|
||||
use sp_blockchain::{HeaderMetadataCache, Result as ClientResult};
|
||||
use sp_database::{Database, Transaction};
|
||||
use codec::{Encode, Decode};
|
||||
use sp_runtime::generic::BlockId;
|
||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero};
|
||||
use crate::utils::{self, COLUMN_META};
|
||||
use crate::DbHash;
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero},
|
||||
};
|
||||
|
||||
use self::list_cache::{ListCache, PruningStrategy};
|
||||
|
||||
@@ -118,7 +128,10 @@ impl<Block: BlockT> DbCache<Block> {
|
||||
}
|
||||
|
||||
/// Begin cache transaction.
|
||||
pub fn transaction<'a>(&'a mut self, tx: &'a mut Transaction<DbHash>) -> DbCacheTransaction<'a, Block> {
|
||||
pub fn transaction<'a>(
|
||||
&'a mut self,
|
||||
tx: &'a mut Transaction<DbHash>,
|
||||
) -> DbCacheTransaction<'a, Block> {
|
||||
DbCacheTransaction {
|
||||
cache: self,
|
||||
tx,
|
||||
@@ -164,7 +177,7 @@ impl<Block: BlockT> DbCache<Block> {
|
||||
self.key_lookup_column,
|
||||
self.header_column,
|
||||
self.cache_column,
|
||||
&self.best_finalized_block
|
||||
&self.best_finalized_block,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -184,19 +197,16 @@ fn get_cache_helper<'a, Block: BlockT>(
|
||||
Entry::Occupied(entry) => Ok(entry.into_mut()),
|
||||
Entry::Vacant(entry) => {
|
||||
let cache = ListCache::new(
|
||||
self::list_storage::DbStorage::new(name.to_vec(), db.clone(),
|
||||
self::list_storage::DbColumns {
|
||||
meta: COLUMN_META,
|
||||
key_lookup,
|
||||
header,
|
||||
cache,
|
||||
},
|
||||
self::list_storage::DbStorage::new(
|
||||
name.to_vec(),
|
||||
db.clone(),
|
||||
self::list_storage::DbColumns { meta: COLUMN_META, key_lookup, header, cache },
|
||||
),
|
||||
cache_pruning_strategy(name),
|
||||
best_finalized_block.clone(),
|
||||
)?;
|
||||
Ok(entry.insert(cache))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,10 +220,7 @@ pub struct DbCacheTransactionOps<Block: BlockT> {
|
||||
impl<Block: BlockT> DbCacheTransactionOps<Block> {
|
||||
/// Empty transaction ops.
|
||||
pub fn empty() -> DbCacheTransactionOps<Block> {
|
||||
DbCacheTransactionOps {
|
||||
cache_at_ops: HashMap::new(),
|
||||
best_finalized_block: None,
|
||||
}
|
||||
DbCacheTransactionOps { cache_at_ops: HashMap::new(), best_finalized_block: None }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,19 +251,21 @@ impl<'a, Block: BlockT> DbCacheTransaction<'a, Block> {
|
||||
) -> ClientResult<Self> {
|
||||
// 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()
|
||||
let missed_caches = self
|
||||
.cache
|
||||
.cache_at
|
||||
.keys()
|
||||
.filter(|cache| !data_at.contains_key(*cache))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut insert_op = |name: CacheKeyId, value: Option<Vec<u8>>| -> Result<(), sp_blockchain::Error> {
|
||||
let mut insert_op = |name: CacheKeyId,
|
||||
value: Option<Vec<u8>>|
|
||||
-> Result<(), sp_blockchain::Error> {
|
||||
let cache = self.cache.get_cache(name)?;
|
||||
let cache_ops = self.cache_at_ops.entry(name).or_default();
|
||||
cache.on_block_insert(
|
||||
&mut self::list_storage::DbStorageTransaction::new(
|
||||
cache.storage(),
|
||||
&mut self.tx,
|
||||
),
|
||||
&mut self::list_storage::DbStorageTransaction::new(cache.storage(), &mut self.tx),
|
||||
parent.clone(),
|
||||
block.clone(),
|
||||
value,
|
||||
@@ -271,8 +280,7 @@ impl<'a, Block: BlockT> DbCacheTransaction<'a, Block> {
|
||||
missed_caches.into_iter().try_for_each(|name| insert_op(name, None))?;
|
||||
|
||||
match entry_type {
|
||||
EntryType::Final | EntryType::Genesis =>
|
||||
self.best_finalized_block = Some(block),
|
||||
EntryType::Final | EntryType::Genesis => self.best_finalized_block = Some(block),
|
||||
EntryType::NonFinal => (),
|
||||
}
|
||||
|
||||
@@ -288,10 +296,7 @@ impl<'a, Block: BlockT> DbCacheTransaction<'a, Block> {
|
||||
for (name, cache) in self.cache.cache_at.iter() {
|
||||
let cache_ops = self.cache_at_ops.entry(*name).or_default();
|
||||
cache.on_block_finalize(
|
||||
&mut self::list_storage::DbStorageTransaction::new(
|
||||
cache.storage(),
|
||||
&mut self.tx
|
||||
),
|
||||
&mut self::list_storage::DbStorageTransaction::new(cache.storage(), &mut self.tx),
|
||||
parent.clone(),
|
||||
block.clone(),
|
||||
cache_ops,
|
||||
@@ -304,17 +309,11 @@ impl<'a, Block: BlockT> DbCacheTransaction<'a, Block> {
|
||||
}
|
||||
|
||||
/// When block is reverted.
|
||||
pub fn on_block_revert(
|
||||
mut self,
|
||||
reverted_block: &ComplexBlockId<Block>,
|
||||
) -> ClientResult<Self> {
|
||||
pub fn on_block_revert(mut self, reverted_block: &ComplexBlockId<Block>) -> ClientResult<Self> {
|
||||
for (name, cache) in self.cache.cache_at.iter() {
|
||||
let cache_ops = self.cache_at_ops.entry(*name).or_default();
|
||||
cache.on_block_revert(
|
||||
&mut self::list_storage::DbStorageTransaction::new(
|
||||
cache.storage(),
|
||||
&mut self.tx
|
||||
),
|
||||
&mut self::list_storage::DbStorageTransaction::new(cache.storage(), &mut self.tx),
|
||||
reverted_block,
|
||||
cache_ops,
|
||||
)?;
|
||||
@@ -352,7 +351,9 @@ impl<Block: BlockT> BlockchainCache<Block> for DbCacheSync<Block> {
|
||||
&self,
|
||||
key: &CacheKeyId,
|
||||
at: &BlockId<Block>,
|
||||
) -> ClientResult<Option<((NumberFor<Block>, Block::Hash), Option<(NumberFor<Block>, Block::Hash)>, Vec<u8>)>> {
|
||||
) -> ClientResult<
|
||||
Option<((NumberFor<Block>, Block::Hash), Option<(NumberFor<Block>, Block::Hash)>, Vec<u8>)>,
|
||||
> {
|
||||
let mut cache = self.0.write();
|
||||
let header_metadata_cache = cache.header_metadata_cache.clone();
|
||||
let cache = cache.get_cache(*key)?;
|
||||
@@ -360,36 +361,39 @@ impl<Block: BlockT> BlockchainCache<Block> for DbCacheSync<Block> {
|
||||
let db = storage.db();
|
||||
let columns = storage.columns();
|
||||
let at = match *at {
|
||||
BlockId::Hash(hash) => {
|
||||
match header_metadata_cache.header_metadata(hash) {
|
||||
Some(metadata) => ComplexBlockId::new(hash, metadata.number),
|
||||
None => {
|
||||
let header = utils::require_header::<Block>(
|
||||
&**db,
|
||||
columns.key_lookup,
|
||||
columns.header,
|
||||
BlockId::Hash(hash.clone()))?;
|
||||
ComplexBlockId::new(hash, *header.number())
|
||||
}
|
||||
}
|
||||
BlockId::Hash(hash) => match header_metadata_cache.header_metadata(hash) {
|
||||
Some(metadata) => ComplexBlockId::new(hash, metadata.number),
|
||||
None => {
|
||||
let header = utils::require_header::<Block>(
|
||||
&**db,
|
||||
columns.key_lookup,
|
||||
columns.header,
|
||||
BlockId::Hash(hash.clone()),
|
||||
)?;
|
||||
ComplexBlockId::new(hash, *header.number())
|
||||
},
|
||||
},
|
||||
BlockId::Number(number) => {
|
||||
let hash = utils::require_header::<Block>(
|
||||
&**db,
|
||||
columns.key_lookup,
|
||||
columns.header,
|
||||
BlockId::Number(number.clone()))?.hash();
|
||||
BlockId::Number(number.clone()),
|
||||
)?
|
||||
.hash();
|
||||
ComplexBlockId::new(hash, number)
|
||||
},
|
||||
};
|
||||
|
||||
cache.value_at_block(&at)
|
||||
.map(|block_and_value| block_and_value.map(|(begin_block, end_block, value)|
|
||||
cache.value_at_block(&at).map(|block_and_value| {
|
||||
block_and_value.map(|(begin_block, end_block, value)| {
|
||||
(
|
||||
(begin_block.number, begin_block.hash),
|
||||
end_block.map(|end_block| (end_block.number, end_block.hash)),
|
||||
value,
|
||||
)))
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,33 +18,43 @@
|
||||
|
||||
//! DB-backed changes tries storage.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use hash_db::Prefix;
|
||||
use codec::{Decode, Encode};
|
||||
use parking_lot::RwLock;
|
||||
use sp_blockchain::{Error as ClientError, Result as ClientResult};
|
||||
use sp_trie::MemoryDB;
|
||||
use sc_client_api::backend::PrunableStateChangesTrieStorage;
|
||||
use sp_blockchain::{well_known_cache_keys, Cache as BlockchainCache, HeaderMetadataCache};
|
||||
use sp_core::{ChangesTrieConfiguration, ChangesTrieConfigurationRange, convert_hash};
|
||||
use sp_core::storage::PrefixedStorageKey;
|
||||
use sp_database::Transaction;
|
||||
use sp_runtime::traits::{
|
||||
Block as BlockT, Header as HeaderT, HashFor, NumberFor, One, Zero, CheckedSub,
|
||||
use crate::{
|
||||
cache::{
|
||||
ComplexBlockId, DbCache, DbCacheSync, DbCacheTransactionOps, EntryType as CacheEntryType,
|
||||
},
|
||||
utils::{self, meta_keys, Meta},
|
||||
Database, DbHash,
|
||||
};
|
||||
use codec::{Decode, Encode};
|
||||
use hash_db::Prefix;
|
||||
use parking_lot::RwLock;
|
||||
use sc_client_api::backend::PrunableStateChangesTrieStorage;
|
||||
use sp_blockchain::{
|
||||
well_known_cache_keys, Cache as BlockchainCache, Error as ClientError, HeaderMetadataCache,
|
||||
Result as ClientResult,
|
||||
};
|
||||
use sp_core::{
|
||||
convert_hash, storage::PrefixedStorageKey, ChangesTrieConfiguration,
|
||||
ChangesTrieConfigurationRange,
|
||||
};
|
||||
use sp_database::Transaction;
|
||||
use sp_runtime::{
|
||||
generic::{BlockId, ChangesTrieSignal, DigestItem},
|
||||
traits::{Block as BlockT, CheckedSub, HashFor, Header as HeaderT, NumberFor, One, Zero},
|
||||
};
|
||||
use sp_runtime::generic::{BlockId, DigestItem, ChangesTrieSignal};
|
||||
use sp_state_machine::{ChangesTrieBuildCache, ChangesTrieCacheAction};
|
||||
use crate::{Database, DbHash};
|
||||
use crate::utils::{self, Meta, meta_keys};
|
||||
use crate::cache::{
|
||||
DbCacheSync, DbCache, DbCacheTransactionOps,
|
||||
ComplexBlockId, EntryType as CacheEntryType,
|
||||
use sp_trie::MemoryDB;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// Extract new changes trie configuration (if available) from the header.
|
||||
pub fn extract_new_configuration<Header: HeaderT>(header: &Header) -> Option<&Option<ChangesTrieConfiguration>> {
|
||||
header.digest()
|
||||
pub fn extract_new_configuration<Header: HeaderT>(
|
||||
header: &Header,
|
||||
) -> Option<&Option<ChangesTrieConfiguration>> {
|
||||
header
|
||||
.digest()
|
||||
.log(DigestItem::as_changes_trie_signal)
|
||||
.and_then(ChangesTrieSignal::as_new_configuration)
|
||||
}
|
||||
@@ -68,10 +78,7 @@ impl<Block: BlockT> DbChangesTrieStorageTransaction<Block> {
|
||||
|
||||
impl<Block: BlockT> From<DbCacheTransactionOps<Block>> for DbChangesTrieStorageTransaction<Block> {
|
||||
fn from(cache_ops: DbCacheTransactionOps<Block>) -> Self {
|
||||
DbChangesTrieStorageTransaction {
|
||||
cache_ops,
|
||||
new_config: None,
|
||||
}
|
||||
DbChangesTrieStorageTransaction { cache_ops, new_config: None }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,21 +180,25 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
|
||||
let new_configuration = match new_configuration {
|
||||
Some(new_configuration) => new_configuration,
|
||||
None if !finalized => return Ok(DbCacheTransactionOps::empty().into()),
|
||||
None => return self.finalize(
|
||||
tx,
|
||||
parent_block.hash,
|
||||
block.hash,
|
||||
block.number,
|
||||
Some(new_header),
|
||||
cache_tx,
|
||||
),
|
||||
None =>
|
||||
return self.finalize(
|
||||
tx,
|
||||
parent_block.hash,
|
||||
block.hash,
|
||||
block.number,
|
||||
Some(new_header),
|
||||
cache_tx,
|
||||
),
|
||||
};
|
||||
|
||||
// update configuration cache
|
||||
let mut cache_at = HashMap::new();
|
||||
cache_at.insert(well_known_cache_keys::CHANGES_TRIE_CONFIG, new_configuration.encode());
|
||||
Ok(DbChangesTrieStorageTransaction::from(match cache_tx {
|
||||
Some(cache_tx) => self.cache.0.write()
|
||||
Some(cache_tx) => self
|
||||
.cache
|
||||
.0
|
||||
.write()
|
||||
.transaction_with_ops(tx, cache_tx.cache_ops)
|
||||
.on_block_insert(
|
||||
parent_block,
|
||||
@@ -196,7 +207,10 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
|
||||
if finalized { CacheEntryType::Final } else { CacheEntryType::NonFinal },
|
||||
)?
|
||||
.into_ops(),
|
||||
None => self.cache.0.write()
|
||||
None => self
|
||||
.cache
|
||||
.0
|
||||
.write()
|
||||
.transaction(tx)
|
||||
.on_block_insert(
|
||||
parent_block,
|
||||
@@ -205,7 +219,8 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
|
||||
if finalized { CacheEntryType::Final } else { CacheEntryType::NonFinal },
|
||||
)?
|
||||
.into_ops(),
|
||||
}).with_new_config(Some(new_configuration)))
|
||||
})
|
||||
.with_new_config(Some(new_configuration)))
|
||||
}
|
||||
|
||||
/// Called when block is finalized.
|
||||
@@ -226,7 +241,7 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
|
||||
if cache_tx.is_some() {
|
||||
if let Some(new_header) = new_header {
|
||||
if new_header.hash() == block_hash {
|
||||
return Ok(cache_tx.expect("guarded by cache_tx.is_some(); qed"));
|
||||
return Ok(cache_tx.expect("guarded by cache_tx.is_some(); qed"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -237,22 +252,21 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
|
||||
let parent_block = ComplexBlockId::new(parent_block_hash, parent_block_num);
|
||||
Ok(match cache_tx {
|
||||
Some(cache_tx) => DbChangesTrieStorageTransaction::from(
|
||||
self.cache.0.write()
|
||||
self.cache
|
||||
.0
|
||||
.write()
|
||||
.transaction_with_ops(tx, cache_tx.cache_ops)
|
||||
.on_block_finalize(
|
||||
parent_block,
|
||||
block,
|
||||
)?
|
||||
.into_ops()
|
||||
).with_new_config(cache_tx.new_config),
|
||||
.on_block_finalize(parent_block, block)?
|
||||
.into_ops(),
|
||||
)
|
||||
.with_new_config(cache_tx.new_config),
|
||||
None => DbChangesTrieStorageTransaction::from(
|
||||
self.cache.0.write()
|
||||
self.cache
|
||||
.0
|
||||
.write()
|
||||
.transaction(tx)
|
||||
.on_block_finalize(
|
||||
parent_block,
|
||||
block,
|
||||
)?
|
||||
.into_ops()
|
||||
.on_block_finalize(parent_block, block)?
|
||||
.into_ops(),
|
||||
),
|
||||
})
|
||||
}
|
||||
@@ -263,23 +277,24 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
|
||||
tx: &mut Transaction<DbHash>,
|
||||
block: &ComplexBlockId<Block>,
|
||||
) -> ClientResult<DbChangesTrieStorageTransaction<Block>> {
|
||||
Ok(self.cache.0.write().transaction(tx)
|
||||
.on_block_revert(block)?
|
||||
.into_ops()
|
||||
.into())
|
||||
Ok(self.cache.0.write().transaction(tx).on_block_revert(block)?.into_ops().into())
|
||||
}
|
||||
|
||||
/// When transaction has been committed.
|
||||
pub fn post_commit(&self, tx: Option<DbChangesTrieStorageTransaction<Block>>) {
|
||||
if let Some(tx) = tx {
|
||||
self.cache.0.write().commit(tx.cache_ops)
|
||||
.expect("only fails if cache with given name isn't loaded yet;\
|
||||
cache is already loaded because there is tx; qed");
|
||||
self.cache.0.write().commit(tx.cache_ops).expect(
|
||||
"only fails if cache with given name isn't loaded yet;\
|
||||
cache is already loaded because there is tx; qed",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Commit changes into changes trie build cache.
|
||||
pub fn commit_build_cache(&self, cache_update: ChangesTrieCacheAction<Block::Hash, NumberFor<Block>>) {
|
||||
pub fn commit_build_cache(
|
||||
&self,
|
||||
cache_update: ChangesTrieCacheAction<Block::Hash, NumberFor<Block>>,
|
||||
) {
|
||||
self.build_cache.write().perform(cache_update);
|
||||
}
|
||||
|
||||
@@ -307,7 +322,7 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
|
||||
// 2) or we are (or were) in period where changes tries are disabled
|
||||
if let Some((begin, end)) = tries_meta.oldest_digest_range {
|
||||
if block_num <= end || block_num - end <= min_blocks_to_keep.into() {
|
||||
break;
|
||||
break
|
||||
}
|
||||
|
||||
tries_meta.oldest_pruned_digest_range_end = end;
|
||||
@@ -333,7 +348,8 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
|
||||
self.key_lookup_column,
|
||||
self.header_column,
|
||||
BlockId::Number(next_digest_range_start),
|
||||
)?.hash(),
|
||||
)?
|
||||
.hash(),
|
||||
};
|
||||
|
||||
let config_for_new_block = new_header
|
||||
@@ -341,21 +357,18 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
|
||||
.unwrap_or(false);
|
||||
let next_config = match cache_tx {
|
||||
Some(cache_tx) if config_for_new_block && cache_tx.new_config.is_some() => {
|
||||
let config = cache_tx
|
||||
.new_config
|
||||
.clone()
|
||||
.expect("guarded by is_some(); qed");
|
||||
let config = cache_tx.new_config.clone().expect("guarded by is_some(); qed");
|
||||
ChangesTrieConfigurationRange {
|
||||
zero: (block_num, block_hash),
|
||||
end: None,
|
||||
config,
|
||||
}
|
||||
},
|
||||
_ if config_for_new_block => {
|
||||
self.configuration_at(&BlockId::Hash(*new_header.expect(
|
||||
"config_for_new_block is only true when new_header is passed; qed"
|
||||
).parent_hash()))?
|
||||
},
|
||||
_ if config_for_new_block => self.configuration_at(&BlockId::Hash(
|
||||
*new_header
|
||||
.expect("config_for_new_block is only true when new_header is passed; qed")
|
||||
.parent_hash(),
|
||||
))?,
|
||||
_ => self.configuration_at(&BlockId::Hash(next_digest_range_start_hash))?,
|
||||
};
|
||||
if let Some(config) = next_config.config {
|
||||
@@ -370,11 +383,11 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
|
||||
}
|
||||
|
||||
tries_meta.oldest_digest_range = Some(oldest_digest_range);
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
tries_meta.oldest_digest_range = None;
|
||||
break;
|
||||
break
|
||||
}
|
||||
|
||||
write_tries_meta(tx, self.meta_column, &*tries_meta);
|
||||
@@ -383,17 +396,23 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
|
||||
}
|
||||
|
||||
impl<Block: BlockT> PrunableStateChangesTrieStorage<Block> for DbChangesTrieStorage<Block> {
|
||||
fn storage(&self) -> &dyn sp_state_machine::ChangesTrieStorage<HashFor<Block>, NumberFor<Block>> {
|
||||
fn storage(
|
||||
&self,
|
||||
) -> &dyn sp_state_machine::ChangesTrieStorage<HashFor<Block>, NumberFor<Block>> {
|
||||
self
|
||||
}
|
||||
|
||||
fn configuration_at(&self, at: &BlockId<Block>) -> ClientResult<
|
||||
ChangesTrieConfigurationRange<NumberFor<Block>, Block::Hash>
|
||||
> {
|
||||
fn configuration_at(
|
||||
&self,
|
||||
at: &BlockId<Block>,
|
||||
) -> ClientResult<ChangesTrieConfigurationRange<NumberFor<Block>, Block::Hash>> {
|
||||
self.cache
|
||||
.get_at(&well_known_cache_keys::CHANGES_TRIE_CONFIG, at)?
|
||||
.and_then(|(zero, end, encoded)| Decode::decode(&mut &encoded[..]).ok()
|
||||
.map(|config| ChangesTrieConfigurationRange { zero, end, config }))
|
||||
.and_then(|(zero, end, encoded)| {
|
||||
Decode::decode(&mut &encoded[..])
|
||||
.ok()
|
||||
.map(|config| ChangesTrieConfigurationRange { zero, end, config })
|
||||
})
|
||||
.ok_or_else(|| ClientError::ErrorReadingChangesTriesConfig)
|
||||
}
|
||||
|
||||
@@ -409,14 +428,21 @@ impl<Block: BlockT> sp_state_machine::ChangesTrieRootsStorage<HashFor<Block>, Nu
|
||||
&self,
|
||||
hash: Block::Hash,
|
||||
) -> Result<sp_state_machine::ChangesTrieAnchorBlockId<Block::Hash, NumberFor<Block>>, String> {
|
||||
utils::read_header::<Block>(&*self.db, self.key_lookup_column, self.header_column, BlockId::Hash(hash))
|
||||
.map_err(|e| e.to_string())
|
||||
.and_then(|maybe_header| maybe_header.map(|header|
|
||||
sp_state_machine::ChangesTrieAnchorBlockId {
|
||||
utils::read_header::<Block>(
|
||||
&*self.db,
|
||||
self.key_lookup_column,
|
||||
self.header_column,
|
||||
BlockId::Hash(hash),
|
||||
)
|
||||
.map_err(|e| e.to_string())
|
||||
.and_then(|maybe_header| {
|
||||
maybe_header
|
||||
.map(|header| sp_state_machine::ChangesTrieAnchorBlockId {
|
||||
hash,
|
||||
number: *header.number(),
|
||||
}
|
||||
).ok_or_else(|| format!("Unknown header: {}", hash)))
|
||||
})
|
||||
.ok_or_else(|| format!("Unknown header: {}", hash))
|
||||
})
|
||||
}
|
||||
|
||||
fn root(
|
||||
@@ -426,7 +452,10 @@ impl<Block: BlockT> sp_state_machine::ChangesTrieRootsStorage<HashFor<Block>, Nu
|
||||
) -> Result<Option<Block::Hash>, String> {
|
||||
// check API requirement: we can't get NEXT block(s) based on anchor
|
||||
if block > anchor.number {
|
||||
return Err(format!("Can't get changes trie root at {} using anchor at {}", block, anchor.number));
|
||||
return Err(format!(
|
||||
"Can't get changes trie root at {} using anchor at {}",
|
||||
block, anchor.number
|
||||
))
|
||||
}
|
||||
|
||||
// we need to get hash of the block to resolve changes trie root
|
||||
@@ -438,8 +467,12 @@ impl<Block: BlockT> sp_state_machine::ChangesTrieRootsStorage<HashFor<Block>, Nu
|
||||
let mut current_num = anchor.number;
|
||||
let mut current_hash: Block::Hash = convert_hash(&anchor.hash);
|
||||
let maybe_anchor_header: Block::Header = utils::require_header::<Block>(
|
||||
&*self.db, self.key_lookup_column, self.header_column, BlockId::Number(current_num)
|
||||
).map_err(|e| e.to_string())?;
|
||||
&*self.db,
|
||||
self.key_lookup_column,
|
||||
self.header_column,
|
||||
BlockId::Number(current_num),
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
if maybe_anchor_header.hash() == current_hash {
|
||||
// if anchor is canonicalized, then the block is also canonicalized
|
||||
BlockId::Number(block)
|
||||
@@ -449,8 +482,12 @@ impl<Block: BlockT> sp_state_machine::ChangesTrieRootsStorage<HashFor<Block>, Nu
|
||||
// back from the anchor to the block with given number
|
||||
while current_num != block {
|
||||
let current_header: Block::Header = utils::require_header::<Block>(
|
||||
&*self.db, self.key_lookup_column, self.header_column, BlockId::Hash(current_hash)
|
||||
).map_err(|e| e.to_string())?;
|
||||
&*self.db,
|
||||
self.key_lookup_column,
|
||||
self.header_column,
|
||||
BlockId::Hash(current_hash),
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
current_hash = *current_header.parent_hash();
|
||||
current_num = current_num - One::one();
|
||||
@@ -460,18 +497,16 @@ impl<Block: BlockT> sp_state_machine::ChangesTrieRootsStorage<HashFor<Block>, Nu
|
||||
}
|
||||
};
|
||||
|
||||
Ok(
|
||||
utils::require_header::<Block>(
|
||||
&*self.db,
|
||||
self.key_lookup_column,
|
||||
self.header_column,
|
||||
block_id,
|
||||
)
|
||||
.map_err(|e| e.to_string())?
|
||||
.digest()
|
||||
.log(DigestItem::as_changes_trie_root)
|
||||
.cloned()
|
||||
Ok(utils::require_header::<Block>(
|
||||
&*self.db,
|
||||
self.key_lookup_column,
|
||||
self.header_column,
|
||||
block_id,
|
||||
)
|
||||
.map_err(|e| e.to_string())?
|
||||
.digest()
|
||||
.log(DigestItem::as_changes_trie_root)
|
||||
.cloned())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,7 +515,9 @@ impl<Block> sp_state_machine::ChangesTrieStorage<HashFor<Block>, NumberFor<Block
|
||||
where
|
||||
Block: BlockT,
|
||||
{
|
||||
fn as_roots_storage(&self) -> &dyn sp_state_machine::ChangesTrieRootsStorage<HashFor<Block>, NumberFor<Block>> {
|
||||
fn as_roots_storage(
|
||||
&self,
|
||||
) -> &dyn sp_state_machine::ChangesTrieRootsStorage<HashFor<Block>, NumberFor<Block>> {
|
||||
self
|
||||
}
|
||||
|
||||
@@ -503,8 +540,9 @@ fn read_tries_meta<Block: BlockT>(
|
||||
meta_column: u32,
|
||||
) -> ClientResult<ChangesTriesMeta<Block>> {
|
||||
match db.get(meta_column, meta_keys::CHANGES_TRIES_META) {
|
||||
Some(h) => Decode::decode(&mut &h[..])
|
||||
.map_err(|err| ClientError::Backend(format!("Error decoding changes tries metadata: {}", err))),
|
||||
Some(h) => Decode::decode(&mut &h[..]).map_err(|err| {
|
||||
ClientError::Backend(format!("Error decoding changes tries metadata: {}", err))
|
||||
}),
|
||||
None => Ok(ChangesTriesMeta {
|
||||
oldest_digest_range: None,
|
||||
oldest_pruned_digest_range_end: Zero::zero(),
|
||||
@@ -523,18 +561,23 @@ fn write_tries_meta<Block: BlockT>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
tests::{insert_header, prepare_changes, Block},
|
||||
Backend,
|
||||
};
|
||||
use hash_db::EMPTY_PREFIX;
|
||||
use sc_client_api::backend::{
|
||||
Backend as ClientBackend, NewBlockState, BlockImportOperation, PrunableStateChangesTrieStorage,
|
||||
Backend as ClientBackend, BlockImportOperation, NewBlockState,
|
||||
PrunableStateChangesTrieStorage,
|
||||
};
|
||||
use sp_blockchain::HeaderBackend as BlockchainHeaderBackend;
|
||||
use sp_core::H256;
|
||||
use sp_runtime::testing::{Digest, Header};
|
||||
use sp_runtime::traits::{Hash, BlakeTwo256};
|
||||
use sp_runtime::{
|
||||
testing::{Digest, Header},
|
||||
traits::{BlakeTwo256, Hash},
|
||||
};
|
||||
use sp_state_machine::{ChangesTrieRootsStorage, ChangesTrieStorage};
|
||||
use crate::Backend;
|
||||
use crate::tests::{Block, insert_header, prepare_changes};
|
||||
use super::*;
|
||||
|
||||
fn changes(number: u64) -> Option<Vec<(Vec<u8>, Vec<u8>)>> {
|
||||
Some(vec![(number.to_le_bytes().to_vec(), number.to_le_bytes().to_vec())])
|
||||
@@ -554,7 +597,9 @@ mod tests {
|
||||
digest.push(DigestItem::ChangesTrieRoot(root));
|
||||
changes_trie_update = update;
|
||||
}
|
||||
digest.push(DigestItem::ChangesTrieSignal(ChangesTrieSignal::NewConfiguration(new_configuration)));
|
||||
digest.push(DigestItem::ChangesTrieSignal(ChangesTrieSignal::NewConfiguration(
|
||||
new_configuration,
|
||||
)));
|
||||
|
||||
let header = Header {
|
||||
number,
|
||||
@@ -573,7 +618,8 @@ mod tests {
|
||||
let mut op = backend.begin_operation().unwrap();
|
||||
backend.begin_state_operation(&mut op, block_id).unwrap();
|
||||
op.set_block_data(header, None, None, None, NewBlockState::Best).unwrap();
|
||||
op.update_changes_trie((changes_trie_update, ChangesTrieCacheAction::Clear)).unwrap();
|
||||
op.update_changes_trie((changes_trie_update, ChangesTrieCacheAction::Clear))
|
||||
.unwrap();
|
||||
backend.commit_operation(op).unwrap();
|
||||
|
||||
header_hash
|
||||
@@ -584,11 +630,13 @@ mod tests {
|
||||
let backend = Backend::<Block>::new_test(1000, 100);
|
||||
backend.changes_tries_storage.meta.write().finalized_number = 1000;
|
||||
|
||||
let check_changes = |backend: &Backend<Block>, block: u64, changes: Vec<(Vec<u8>, Vec<u8>)>| {
|
||||
let check_changes = |backend: &Backend<Block>,
|
||||
block: u64,
|
||||
changes: Vec<(Vec<u8>, Vec<u8>)>| {
|
||||
let (changes_root, mut changes_trie_update) = prepare_changes(changes);
|
||||
let anchor = sp_state_machine::ChangesTrieAnchorBlockId {
|
||||
hash: backend.blockchain().header(BlockId::Number(block)).unwrap().unwrap().hash(),
|
||||
number: block
|
||||
number: block,
|
||||
};
|
||||
assert_eq!(backend.changes_tries_storage.root(&anchor, block), Ok(Some(changes_root)));
|
||||
|
||||
@@ -605,7 +653,13 @@ mod tests {
|
||||
];
|
||||
let changes2 = vec![(b"key_at_2".to_vec(), b"val_at_2".to_vec())];
|
||||
|
||||
let block0 = insert_header(&backend, 0, Default::default(), Some(changes0.clone()), Default::default());
|
||||
let block0 = insert_header(
|
||||
&backend,
|
||||
0,
|
||||
Default::default(),
|
||||
Some(changes0.clone()),
|
||||
Default::default(),
|
||||
);
|
||||
let block1 = insert_header(&backend, 1, block0, Some(changes1.clone()), Default::default());
|
||||
let _ = insert_header(&backend, 2, block1, Some(changes2.clone()), Default::default());
|
||||
|
||||
@@ -622,19 +676,29 @@ mod tests {
|
||||
let changes0 = vec![(b"k0".to_vec(), b"v0".to_vec())];
|
||||
let changes1 = vec![(b"k1".to_vec(), b"v1".to_vec())];
|
||||
let changes2 = vec![(b"k2".to_vec(), b"v2".to_vec())];
|
||||
let block0 = insert_header(&backend, 0, Default::default(), Some(changes0.clone()), Default::default());
|
||||
let block0 = insert_header(
|
||||
&backend,
|
||||
0,
|
||||
Default::default(),
|
||||
Some(changes0.clone()),
|
||||
Default::default(),
|
||||
);
|
||||
let block1 = insert_header(&backend, 1, block0, Some(changes1.clone()), Default::default());
|
||||
let block2 = insert_header(&backend, 2, block1, Some(changes2.clone()), Default::default());
|
||||
|
||||
let changes2_1_0 = vec![(b"k3".to_vec(), b"v3".to_vec())];
|
||||
let changes2_1_1 = vec![(b"k4".to_vec(), b"v4".to_vec())];
|
||||
let block2_1_0 = insert_header(&backend, 3, block2, Some(changes2_1_0.clone()), Default::default());
|
||||
let block2_1_1 = insert_header(&backend, 4, block2_1_0, Some(changes2_1_1.clone()), Default::default());
|
||||
let block2_1_0 =
|
||||
insert_header(&backend, 3, block2, Some(changes2_1_0.clone()), Default::default());
|
||||
let block2_1_1 =
|
||||
insert_header(&backend, 4, block2_1_0, Some(changes2_1_1.clone()), Default::default());
|
||||
|
||||
let changes2_2_0 = vec![(b"k5".to_vec(), b"v5".to_vec())];
|
||||
let changes2_2_1 = vec![(b"k6".to_vec(), b"v6".to_vec())];
|
||||
let block2_2_0 = insert_header(&backend, 3, block2, Some(changes2_2_0.clone()), Default::default());
|
||||
let block2_2_1 = insert_header(&backend, 4, block2_2_0, Some(changes2_2_1.clone()), Default::default());
|
||||
let block2_2_0 =
|
||||
insert_header(&backend, 3, block2, Some(changes2_2_0.clone()), Default::default());
|
||||
let block2_2_1 =
|
||||
insert_header(&backend, 4, block2_2_0, Some(changes2_2_1.clone()), Default::default());
|
||||
|
||||
// finalize block1
|
||||
backend.changes_tries_storage.meta.write().finalized_number = 1;
|
||||
@@ -680,7 +744,12 @@ mod tests {
|
||||
if number == 0 {
|
||||
Default::default()
|
||||
} else {
|
||||
backend.blockchain().header(BlockId::Number(number - 1)).unwrap().unwrap().hash()
|
||||
backend
|
||||
.blockchain()
|
||||
.header(BlockId::Number(number - 1))
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.hash()
|
||||
}
|
||||
};
|
||||
|
||||
@@ -698,12 +767,14 @@ mod tests {
|
||||
let trie_root = backend
|
||||
.blockchain()
|
||||
.header(BlockId::Number(number))
|
||||
.unwrap().unwrap()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.digest()
|
||||
.log(DigestItem::as_changes_trie_root)
|
||||
.cloned();
|
||||
match trie_root {
|
||||
Some(trie_root) => backend.changes_tries_storage.get(&trie_root, EMPTY_PREFIX).unwrap().is_none(),
|
||||
Some(trie_root) =>
|
||||
backend.changes_tries_storage.get(&trie_root, EMPTY_PREFIX).unwrap().is_none(),
|
||||
None => true,
|
||||
}
|
||||
};
|
||||
@@ -711,14 +782,10 @@ mod tests {
|
||||
let finalize_block = |number| {
|
||||
let header = backend.blockchain().header(BlockId::Number(number)).unwrap().unwrap();
|
||||
let mut tx = Transaction::new();
|
||||
let cache_ops = backend.changes_tries_storage.finalize(
|
||||
&mut tx,
|
||||
*header.parent_hash(),
|
||||
header.hash(),
|
||||
number,
|
||||
None,
|
||||
None,
|
||||
).unwrap();
|
||||
let cache_ops = backend
|
||||
.changes_tries_storage
|
||||
.finalize(&mut tx, *header.parent_hash(), header.hash(), number, None, None)
|
||||
.unwrap();
|
||||
backend.storage.db.commit(tx).unwrap();
|
||||
backend.changes_tries_storage.post_commit(Some(cache_ops));
|
||||
};
|
||||
@@ -737,11 +804,23 @@ mod tests {
|
||||
(0..6).for_each(|number| insert_regular_header(false, number));
|
||||
insert_header_with_configuration_change(&backend, 6, parent_hash(6), None, config_at_6);
|
||||
(7..17).for_each(|number| insert_regular_header(true, number));
|
||||
insert_header_with_configuration_change(&backend, 17, parent_hash(17), changes(17), config_at_17);
|
||||
insert_header_with_configuration_change(
|
||||
&backend,
|
||||
17,
|
||||
parent_hash(17),
|
||||
changes(17),
|
||||
config_at_17,
|
||||
);
|
||||
(18..21).for_each(|number| insert_regular_header(false, number));
|
||||
insert_header_with_configuration_change(&backend, 21, parent_hash(21), None, config_at_21);
|
||||
(22..32).for_each(|number| insert_regular_header(true, number));
|
||||
insert_header_with_configuration_change(&backend, 32, parent_hash(32), changes(32), config_at_32);
|
||||
insert_header_with_configuration_change(
|
||||
&backend,
|
||||
32,
|
||||
parent_hash(32),
|
||||
changes(32),
|
||||
config_at_32,
|
||||
);
|
||||
(33..50).for_each(|number| insert_regular_header(true, number));
|
||||
|
||||
// when only genesis is finalized, nothing is pruned
|
||||
@@ -826,29 +905,24 @@ mod tests {
|
||||
let backend = Backend::<Block>::new_test(1000, 100);
|
||||
|
||||
// configurations at blocks
|
||||
let config_at_1 = Some(ChangesTrieConfiguration {
|
||||
digest_interval: 4,
|
||||
digest_levels: 2,
|
||||
});
|
||||
let config_at_3 = Some(ChangesTrieConfiguration {
|
||||
digest_interval: 8,
|
||||
digest_levels: 1,
|
||||
});
|
||||
let config_at_1 = Some(ChangesTrieConfiguration { digest_interval: 4, digest_levels: 2 });
|
||||
let config_at_3 = Some(ChangesTrieConfiguration { digest_interval: 8, digest_levels: 1 });
|
||||
let config_at_5 = None;
|
||||
let config_at_7 = Some(ChangesTrieConfiguration {
|
||||
digest_interval: 8,
|
||||
digest_levels: 1,
|
||||
});
|
||||
let config_at_7 = Some(ChangesTrieConfiguration { digest_interval: 8, digest_levels: 1 });
|
||||
|
||||
// insert some blocks
|
||||
let block0 = insert_header(&backend, 0, Default::default(), None, Default::default());
|
||||
let block1 = insert_header_with_configuration_change(&backend, 1, block0, None, config_at_1.clone());
|
||||
let block1 =
|
||||
insert_header_with_configuration_change(&backend, 1, block0, None, config_at_1.clone());
|
||||
let block2 = insert_header(&backend, 2, block1, None, Default::default());
|
||||
let block3 = insert_header_with_configuration_change(&backend, 3, block2, None, config_at_3.clone());
|
||||
let block3 =
|
||||
insert_header_with_configuration_change(&backend, 3, block2, None, config_at_3.clone());
|
||||
let block4 = insert_header(&backend, 4, block3, None, Default::default());
|
||||
let block5 = insert_header_with_configuration_change(&backend, 5, block4, None, config_at_5.clone());
|
||||
let block5 =
|
||||
insert_header_with_configuration_change(&backend, 5, block4, None, config_at_5.clone());
|
||||
let block6 = insert_header(&backend, 6, block5, None, Default::default());
|
||||
let block7 = insert_header_with_configuration_change(&backend, 7, block6, None, config_at_7.clone());
|
||||
let block7 =
|
||||
insert_header_with_configuration_change(&backend, 7, block6, None, config_at_7.clone());
|
||||
|
||||
// test configuration cache
|
||||
let storage = &backend.changes_tries_storage;
|
||||
@@ -887,17 +961,48 @@ mod tests {
|
||||
let mut backend = Backend::<Block>::new_test(10, 10);
|
||||
backend.changes_tries_storage.min_blocks_to_keep = Some(8);
|
||||
|
||||
let configs = (0..=7).map(|i| Some(ChangesTrieConfiguration::new(2, i))).collect::<Vec<_>>();
|
||||
let configs =
|
||||
(0..=7).map(|i| Some(ChangesTrieConfiguration::new(2, i))).collect::<Vec<_>>();
|
||||
|
||||
// insert unfinalized headers
|
||||
let block0 = insert_header_with_configuration_change(&backend, 0, Default::default(), None, configs[0].clone());
|
||||
let block1 = insert_header_with_configuration_change(&backend, 1, block0, changes(1), configs[1].clone());
|
||||
let block2 = insert_header_with_configuration_change(&backend, 2, block1, changes(2), configs[2].clone());
|
||||
let block0 = insert_header_with_configuration_change(
|
||||
&backend,
|
||||
0,
|
||||
Default::default(),
|
||||
None,
|
||||
configs[0].clone(),
|
||||
);
|
||||
let block1 = insert_header_with_configuration_change(
|
||||
&backend,
|
||||
1,
|
||||
block0,
|
||||
changes(1),
|
||||
configs[1].clone(),
|
||||
);
|
||||
let block2 = insert_header_with_configuration_change(
|
||||
&backend,
|
||||
2,
|
||||
block1,
|
||||
changes(2),
|
||||
configs[2].clone(),
|
||||
);
|
||||
|
||||
let side_config2_1 = Some(ChangesTrieConfiguration::new(3, 2));
|
||||
let side_config2_2 = Some(ChangesTrieConfiguration::new(3, 3));
|
||||
let block2_1 = insert_header_with_configuration_change(&backend, 2, block1, changes(8), side_config2_1.clone());
|
||||
let _ = insert_header_with_configuration_change(&backend, 3, block2_1, changes(9), side_config2_2.clone());
|
||||
let block2_1 = insert_header_with_configuration_change(
|
||||
&backend,
|
||||
2,
|
||||
block1,
|
||||
changes(8),
|
||||
side_config2_1.clone(),
|
||||
);
|
||||
let _ = insert_header_with_configuration_change(
|
||||
&backend,
|
||||
3,
|
||||
block2_1,
|
||||
changes(9),
|
||||
side_config2_2.clone(),
|
||||
);
|
||||
|
||||
// insert finalized header => 4 headers are finalized at once
|
||||
let header3 = Header {
|
||||
@@ -905,9 +1010,9 @@ mod tests {
|
||||
parent_hash: block2,
|
||||
state_root: Default::default(),
|
||||
digest: Digest {
|
||||
logs: vec![
|
||||
DigestItem::ChangesTrieSignal(ChangesTrieSignal::NewConfiguration(configs[3].clone())),
|
||||
],
|
||||
logs: vec![DigestItem::ChangesTrieSignal(ChangesTrieSignal::NewConfiguration(
|
||||
configs[3].clone(),
|
||||
))],
|
||||
},
|
||||
extrinsics_root: Default::default(),
|
||||
};
|
||||
@@ -920,9 +1025,27 @@ mod tests {
|
||||
backend.commit_operation(op).unwrap();
|
||||
|
||||
// insert more unfinalized headers
|
||||
let block4 = insert_header_with_configuration_change(&backend, 4, block3, changes(4), configs[4].clone());
|
||||
let block5 = insert_header_with_configuration_change(&backend, 5, block4, changes(5), configs[5].clone());
|
||||
let block6 = insert_header_with_configuration_change(&backend, 6, block5, changes(6), configs[6].clone());
|
||||
let block4 = insert_header_with_configuration_change(
|
||||
&backend,
|
||||
4,
|
||||
block3,
|
||||
changes(4),
|
||||
configs[4].clone(),
|
||||
);
|
||||
let block5 = insert_header_with_configuration_change(
|
||||
&backend,
|
||||
5,
|
||||
block4,
|
||||
changes(5),
|
||||
configs[5].clone(),
|
||||
);
|
||||
let block6 = insert_header_with_configuration_change(
|
||||
&backend,
|
||||
6,
|
||||
block5,
|
||||
changes(6),
|
||||
configs[6].clone(),
|
||||
);
|
||||
|
||||
// insert finalized header => 4 headers are finalized at once
|
||||
let header7 = Header {
|
||||
@@ -930,9 +1053,9 @@ mod tests {
|
||||
parent_hash: block6,
|
||||
state_root: Default::default(),
|
||||
digest: Digest {
|
||||
logs: vec![
|
||||
DigestItem::ChangesTrieSignal(ChangesTrieSignal::NewConfiguration(configs[7].clone())),
|
||||
],
|
||||
logs: vec![DigestItem::ChangesTrieSignal(ChangesTrieSignal::NewConfiguration(
|
||||
configs[7].clone(),
|
||||
))],
|
||||
},
|
||||
extrinsics_root: Default::default(),
|
||||
};
|
||||
@@ -950,23 +1073,33 @@ mod tests {
|
||||
let backend = Backend::<Block>::new_test(10, 10);
|
||||
|
||||
let config0 = Some(ChangesTrieConfiguration::new(2, 5));
|
||||
let block0 = insert_header_with_configuration_change(&backend, 0, Default::default(), None, config0);
|
||||
let block0 =
|
||||
insert_header_with_configuration_change(&backend, 0, Default::default(), None, config0);
|
||||
let config1 = Some(ChangesTrieConfiguration::new(2, 6));
|
||||
let block1 = insert_header_with_configuration_change(&backend, 1, block0, changes(0), config1);
|
||||
let block1 =
|
||||
insert_header_with_configuration_change(&backend, 1, block0, changes(0), config1);
|
||||
let just1 = Some((*b"TEST", vec![42]));
|
||||
backend.finalize_block(BlockId::Number(1), just1).unwrap();
|
||||
let config2 = Some(ChangesTrieConfiguration::new(2, 7));
|
||||
let block2 = insert_header_with_configuration_change(&backend, 2, block1, changes(1), config2);
|
||||
let block2 =
|
||||
insert_header_with_configuration_change(&backend, 2, block1, changes(1), config2);
|
||||
let config2_1 = Some(ChangesTrieConfiguration::new(2, 8));
|
||||
let _ = insert_header_with_configuration_change(&backend, 3, block2, changes(10), config2_1);
|
||||
let _ =
|
||||
insert_header_with_configuration_change(&backend, 3, block2, changes(10), config2_1);
|
||||
let config2_2 = Some(ChangesTrieConfiguration::new(2, 9));
|
||||
let block2_2 = insert_header_with_configuration_change(&backend, 3, block2, changes(20), config2_2);
|
||||
let block2_2 =
|
||||
insert_header_with_configuration_change(&backend, 3, block2, changes(20), config2_2);
|
||||
let config2_3 = Some(ChangesTrieConfiguration::new(2, 10));
|
||||
let _ = insert_header_with_configuration_change(&backend, 4, block2_2, changes(30), config2_3);
|
||||
let _ =
|
||||
insert_header_with_configuration_change(&backend, 4, block2_2, changes(30), config2_3);
|
||||
|
||||
// before truncate there are 2 unfinalized forks - block2_1+block2_3
|
||||
assert_eq!(
|
||||
backend.changes_tries_storage.cache.0.write()
|
||||
backend
|
||||
.changes_tries_storage
|
||||
.cache
|
||||
.0
|
||||
.write()
|
||||
.get_cache(well_known_cache_keys::CHANGES_TRIE_CONFIG)
|
||||
.unwrap()
|
||||
.unfinalized()
|
||||
@@ -979,7 +1112,11 @@ mod tests {
|
||||
// after truncating block2_3 - there are 2 unfinalized forks - block2_1+block2_2
|
||||
backend.revert(1, false).unwrap();
|
||||
assert_eq!(
|
||||
backend.changes_tries_storage.cache.0.write()
|
||||
backend
|
||||
.changes_tries_storage
|
||||
.cache
|
||||
.0
|
||||
.write()
|
||||
.get_cache(well_known_cache_keys::CHANGES_TRIE_CONFIG)
|
||||
.unwrap()
|
||||
.unfinalized()
|
||||
@@ -993,7 +1130,11 @@ mod tests {
|
||||
// the 1st one points to the block #3 because it isn't truncated
|
||||
backend.revert(1, false).unwrap();
|
||||
assert_eq!(
|
||||
backend.changes_tries_storage.cache.0.write()
|
||||
backend
|
||||
.changes_tries_storage
|
||||
.cache
|
||||
.0
|
||||
.write()
|
||||
.get_cache(well_known_cache_keys::CHANGES_TRIE_CONFIG)
|
||||
.unwrap()
|
||||
.unfinalized()
|
||||
@@ -1005,15 +1146,17 @@ mod tests {
|
||||
|
||||
// after truncating block2 - there are no unfinalized forks
|
||||
backend.revert(1, false).unwrap();
|
||||
assert!(
|
||||
backend.changes_tries_storage.cache.0.write()
|
||||
.get_cache(well_known_cache_keys::CHANGES_TRIE_CONFIG)
|
||||
.unwrap()
|
||||
.unfinalized()
|
||||
.iter()
|
||||
.map(|fork| fork.head().valid_from.number)
|
||||
.collect::<Vec<_>>()
|
||||
.is_empty(),
|
||||
);
|
||||
assert!(backend
|
||||
.changes_tries_storage
|
||||
.cache
|
||||
.0
|
||||
.write()
|
||||
.get_cache(well_known_cache_keys::CHANGES_TRIE_CONFIG)
|
||||
.unwrap()
|
||||
.unfinalized()
|
||||
.iter()
|
||||
.map(|fork| fork.head().valid_from.number)
|
||||
.collect::<Vec<_>>()
|
||||
.is_empty(),);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,17 +18,22 @@
|
||||
|
||||
//! Functionality for reading and storing children hashes from db.
|
||||
|
||||
use codec::{Encode, Decode};
|
||||
use sp_blockchain;
|
||||
use std::hash::Hash;
|
||||
use sp_database::{Database, Transaction};
|
||||
use crate::DbHash;
|
||||
use codec::{Decode, Encode};
|
||||
use sp_blockchain;
|
||||
use sp_database::{Database, Transaction};
|
||||
use std::hash::Hash;
|
||||
|
||||
/// Returns the hashes of the children blocks of the block with `parent_hash`.
|
||||
pub fn read_children<
|
||||
K: Eq + Hash + Clone + Encode + Decode,
|
||||
V: Eq + Hash + Clone + Encode + Decode,
|
||||
>(db: &dyn Database<DbHash>, column: u32, prefix: &[u8], parent_hash: K) -> sp_blockchain::Result<Vec<V>> {
|
||||
>(
|
||||
db: &dyn Database<DbHash>,
|
||||
column: u32,
|
||||
prefix: &[u8],
|
||||
parent_hash: K,
|
||||
) -> sp_blockchain::Result<Vec<V>> {
|
||||
let mut buf = prefix.to_vec();
|
||||
parent_hash.using_encoded(|s| buf.extend(s));
|
||||
|
||||
@@ -65,9 +70,7 @@ pub fn write_children<
|
||||
}
|
||||
|
||||
/// Prepare transaction to remove the children of `parent_hash`.
|
||||
pub fn remove_children<
|
||||
K: Eq + Hash + Clone + Encode + Decode,
|
||||
>(
|
||||
pub fn remove_children<K: Eq + Hash + Clone + Encode + Decode>(
|
||||
tx: &mut Transaction<DbHash>,
|
||||
column: u32,
|
||||
prefix: &[u8],
|
||||
@@ -78,7 +81,6 @@ pub fn remove_children<
|
||||
tx.remove(column, &key);
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
+655
-631
File diff suppressed because it is too large
Load Diff
+294
-203
@@ -18,31 +18,31 @@
|
||||
|
||||
//! RocksDB-based light client blockchain storage.
|
||||
|
||||
use std::{sync::Arc, collections::HashMap};
|
||||
use std::convert::TryInto;
|
||||
use parking_lot::RwLock;
|
||||
use std::{collections::HashMap, convert::TryInto, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
cache::{ComplexBlockId, DbCache, DbCacheSync, EntryType as CacheEntryType},
|
||||
utils::{self, block_id_to_lookup_key, meta_keys, read_db, read_meta, DatabaseType, Meta},
|
||||
DatabaseSettings, DbHash, FrozenForDuration,
|
||||
};
|
||||
use codec::{Decode, Encode};
|
||||
use log::{debug, trace, warn};
|
||||
use sc_client_api::{
|
||||
cht, backend::{AuxStore, NewBlockState, ProvideChtRoots}, UsageInfo,
|
||||
blockchain::{
|
||||
BlockStatus, Cache as BlockchainCache, Info as BlockchainInfo,
|
||||
},
|
||||
Storage,
|
||||
backend::{AuxStore, NewBlockState, ProvideChtRoots},
|
||||
blockchain::{BlockStatus, Cache as BlockchainCache, Info as BlockchainInfo},
|
||||
cht, Storage, UsageInfo,
|
||||
};
|
||||
use sp_blockchain::{
|
||||
CachedHeaderMetadata, HeaderMetadata, HeaderMetadataCache,
|
||||
Error as ClientError, Result as ClientResult,
|
||||
HeaderBackend as BlockchainHeaderBackend,
|
||||
well_known_cache_keys,
|
||||
well_known_cache_keys, CachedHeaderMetadata, Error as ClientError,
|
||||
HeaderBackend as BlockchainHeaderBackend, HeaderMetadata, HeaderMetadataCache,
|
||||
Result as ClientResult,
|
||||
};
|
||||
use sp_database::{Database, Transaction};
|
||||
use codec::{Decode, Encode};
|
||||
use sp_runtime::generic::{DigestItem, BlockId};
|
||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, Zero, One, NumberFor, HashFor};
|
||||
use crate::cache::{DbCacheSync, DbCache, ComplexBlockId, EntryType as CacheEntryType};
|
||||
use crate::utils::{self, meta_keys, DatabaseType, Meta, read_db, block_id_to_lookup_key, read_meta};
|
||||
use crate::{DatabaseSettings, FrozenForDuration, DbHash};
|
||||
use log::{trace, warn, debug};
|
||||
use sp_runtime::{
|
||||
generic::{BlockId, DigestItem},
|
||||
traits::{Block as BlockT, HashFor, Header as HeaderT, NumberFor, One, Zero},
|
||||
};
|
||||
|
||||
pub(crate) mod columns {
|
||||
pub const META: u32 = crate::utils::COLUMN_META;
|
||||
@@ -139,8 +139,8 @@ impl<Block: BlockT> LightStorage<Block> {
|
||||
}
|
||||
|
||||
impl<Block> BlockchainHeaderBackend<Block> for LightStorage<Block>
|
||||
where
|
||||
Block: BlockT,
|
||||
where
|
||||
Block: BlockT,
|
||||
{
|
||||
fn header(&self, id: BlockId<Block>) -> ClientResult<Option<Block::Header>> {
|
||||
utils::read_header(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id)
|
||||
@@ -165,12 +165,8 @@ impl<Block> BlockchainHeaderBackend<Block> for LightStorage<Block>
|
||||
|
||||
fn status(&self, id: BlockId<Block>) -> ClientResult<BlockStatus> {
|
||||
let exists = match id {
|
||||
BlockId::Hash(_) => read_db(
|
||||
&*self.db,
|
||||
columns::KEY_LOOKUP,
|
||||
columns::HEADER,
|
||||
id
|
||||
)?.is_some(),
|
||||
BlockId::Hash(_) =>
|
||||
read_db(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id)?.is_some(),
|
||||
BlockId::Number(n) => n <= self.meta.read().best_number,
|
||||
};
|
||||
match exists {
|
||||
@@ -180,7 +176,9 @@ impl<Block> BlockchainHeaderBackend<Block> for LightStorage<Block>
|
||||
}
|
||||
|
||||
fn number(&self, hash: Block::Hash) -> ClientResult<Option<NumberFor<Block>>> {
|
||||
if let Some(lookup_key) = block_id_to_lookup_key::<Block>(&*self.db, columns::KEY_LOOKUP, BlockId::Hash(hash))? {
|
||||
if let Some(lookup_key) =
|
||||
block_id_to_lookup_key::<Block>(&*self.db, columns::KEY_LOOKUP, BlockId::Hash(hash))?
|
||||
{
|
||||
let number = utils::lookup_key_to_number(&lookup_key)?;
|
||||
Ok(Some(number))
|
||||
} else {
|
||||
@@ -196,17 +194,25 @@ impl<Block> BlockchainHeaderBackend<Block> for LightStorage<Block>
|
||||
impl<Block: BlockT> HeaderMetadata<Block> for LightStorage<Block> {
|
||||
type Error = ClientError;
|
||||
|
||||
fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
|
||||
self.header_metadata_cache.header_metadata(hash).map_or_else(|| {
|
||||
self.header(BlockId::hash(hash))?.map(|header| {
|
||||
let header_metadata = CachedHeaderMetadata::from(&header);
|
||||
self.header_metadata_cache.insert_header_metadata(
|
||||
header_metadata.hash,
|
||||
header_metadata.clone(),
|
||||
);
|
||||
header_metadata
|
||||
}).ok_or_else(|| ClientError::UnknownBlock(format!("header not found in db: {}", hash)))
|
||||
}, Ok)
|
||||
fn header_metadata(
|
||||
&self,
|
||||
hash: Block::Hash,
|
||||
) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
|
||||
self.header_metadata_cache.header_metadata(hash).map_or_else(
|
||||
|| {
|
||||
self.header(BlockId::hash(hash))?
|
||||
.map(|header| {
|
||||
let header_metadata = CachedHeaderMetadata::from(&header);
|
||||
self.header_metadata_cache
|
||||
.insert_header_metadata(header_metadata.hash, header_metadata.clone());
|
||||
header_metadata
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
ClientError::UnknownBlock(format!("header not found in db: {}", hash))
|
||||
})
|
||||
},
|
||||
Ok,
|
||||
)
|
||||
}
|
||||
|
||||
fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
|
||||
@@ -221,10 +227,9 @@ impl<Block: BlockT> HeaderMetadata<Block> for LightStorage<Block> {
|
||||
impl<Block: BlockT> LightStorage<Block> {
|
||||
// Get block changes trie root, if available.
|
||||
fn changes_trie_root(&self, block: BlockId<Block>) -> ClientResult<Option<Block::Hash>> {
|
||||
self.header(block)
|
||||
.map(|header| header.and_then(|header|
|
||||
header.digest().log(DigestItem::as_changes_trie_root)
|
||||
.cloned()))
|
||||
self.header(block).map(|header| {
|
||||
header.and_then(|header| header.digest().log(DigestItem::as_changes_trie_root).cloned())
|
||||
})
|
||||
}
|
||||
|
||||
/// Handle setting head within a transaction. `route_to` should be the last
|
||||
@@ -251,14 +256,16 @@ impl<Block: BlockT> LightStorage<Block> {
|
||||
for retracted in tree_route.retracted() {
|
||||
if retracted.hash == meta.finalized_hash {
|
||||
// TODO: can we recover here?
|
||||
warn!("Safety failure: reverting finalized block {:?}",
|
||||
(&retracted.number, &retracted.hash));
|
||||
warn!(
|
||||
"Safety failure: reverting finalized block {:?}",
|
||||
(&retracted.number, &retracted.hash)
|
||||
);
|
||||
}
|
||||
|
||||
utils::remove_number_to_key_mapping(
|
||||
transaction,
|
||||
columns::KEY_LOOKUP,
|
||||
retracted.number
|
||||
retracted.number,
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -267,7 +274,7 @@ impl<Block: BlockT> LightStorage<Block> {
|
||||
transaction,
|
||||
columns::KEY_LOOKUP,
|
||||
enacted.number,
|
||||
enacted.hash
|
||||
enacted.hash,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
@@ -292,10 +299,11 @@ impl<Block: BlockT> LightStorage<Block> {
|
||||
) -> ClientResult<()> {
|
||||
let meta = self.meta.read();
|
||||
if &meta.finalized_hash != header.parent_hash() {
|
||||
return Err(::sp_blockchain::Error::NonSequentialFinalization(
|
||||
format!("Last finalized {:?} not parent of {:?}",
|
||||
meta.finalized_hash, hash),
|
||||
).into())
|
||||
return Err(::sp_blockchain::Error::NonSequentialFinalization(format!(
|
||||
"Last finalized {:?} not parent of {:?}",
|
||||
meta.finalized_hash, hash
|
||||
))
|
||||
.into())
|
||||
}
|
||||
|
||||
let lookup_key = utils::number_and_hash_to_lookup_key(header.number().clone(), hash)?;
|
||||
@@ -313,12 +321,14 @@ impl<Block: BlockT> LightStorage<Block> {
|
||||
});
|
||||
|
||||
let new_header_cht_root = cht::compute_root::<Block::Header, HashFor<Block>, _>(
|
||||
cht::size(), new_cht_number, cht_range.map(|num| self.hash(num))
|
||||
cht::size(),
|
||||
new_cht_number,
|
||||
cht_range.map(|num| self.hash(num)),
|
||||
)?;
|
||||
transaction.set(
|
||||
columns::CHT,
|
||||
&cht_key(HEADER_CHT_PREFIX, new_cht_start)?,
|
||||
new_header_cht_root.as_ref()
|
||||
new_header_cht_root.as_ref(),
|
||||
);
|
||||
|
||||
// if the header includes changes trie root, let's build a changes tries roots CHT
|
||||
@@ -329,14 +339,16 @@ impl<Block: BlockT> LightStorage<Block> {
|
||||
current_num = current_num + One::one();
|
||||
Some(old_current_num)
|
||||
});
|
||||
let new_changes_trie_cht_root = cht::compute_root::<Block::Header, HashFor<Block>, _>(
|
||||
cht::size(), new_cht_number, cht_range
|
||||
.map(|num| self.changes_trie_root(BlockId::Number(num)))
|
||||
)?;
|
||||
let new_changes_trie_cht_root =
|
||||
cht::compute_root::<Block::Header, HashFor<Block>, _>(
|
||||
cht::size(),
|
||||
new_cht_number,
|
||||
cht_range.map(|num| self.changes_trie_root(BlockId::Number(num))),
|
||||
)?;
|
||||
transaction.set(
|
||||
columns::CHT,
|
||||
&cht_key(CHANGES_TRIE_CHT_PREFIX, new_cht_start)?,
|
||||
new_changes_trie_cht_root.as_ref()
|
||||
new_changes_trie_cht_root.as_ref(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -354,7 +366,7 @@ impl<Block: BlockT> LightStorage<Block> {
|
||||
transaction,
|
||||
columns::KEY_LOOKUP,
|
||||
prune_block,
|
||||
hash
|
||||
hash,
|
||||
)?;
|
||||
transaction.remove(columns::HEADER, &lookup_key);
|
||||
}
|
||||
@@ -370,7 +382,7 @@ impl<Block: BlockT> LightStorage<Block> {
|
||||
&self,
|
||||
cht_type: u8,
|
||||
cht_size: NumberFor<Block>,
|
||||
block: NumberFor<Block>
|
||||
block: NumberFor<Block>,
|
||||
) -> ClientResult<Option<Block::Hash>> {
|
||||
let no_cht_for_block = || ClientError::Backend(format!("Missing CHT for block {}", block));
|
||||
|
||||
@@ -383,7 +395,8 @@ impl<Block: BlockT> LightStorage<Block> {
|
||||
}
|
||||
|
||||
let cht_start = cht::start_number(cht_size, cht_number);
|
||||
self.db.get(columns::CHT, &cht_key(cht_type, cht_start)?)
|
||||
self.db
|
||||
.get(columns::CHT, &cht_key(cht_type, cht_start)?)
|
||||
.ok_or_else(no_cht_for_block)
|
||||
.and_then(|hash| Block::Hash::decode(&mut &*hash).map_err(|_| no_cht_for_block()))
|
||||
.map(Some)
|
||||
@@ -391,15 +404,20 @@ impl<Block: BlockT> LightStorage<Block> {
|
||||
}
|
||||
|
||||
impl<Block> AuxStore for LightStorage<Block>
|
||||
where Block: BlockT,
|
||||
where
|
||||
Block: BlockT,
|
||||
{
|
||||
fn insert_aux<
|
||||
'a,
|
||||
'b: 'a,
|
||||
'c: 'a,
|
||||
I: IntoIterator<Item=&'a(&'c [u8], &'c [u8])>,
|
||||
D: IntoIterator<Item=&'a &'b [u8]>,
|
||||
>(&self, insert: I, delete: D) -> ClientResult<()> {
|
||||
I: IntoIterator<Item = &'a (&'c [u8], &'c [u8])>,
|
||||
D: IntoIterator<Item = &'a &'b [u8]>,
|
||||
>(
|
||||
&self,
|
||||
insert: I,
|
||||
delete: D,
|
||||
) -> ClientResult<()> {
|
||||
let mut transaction = Transaction::new();
|
||||
for (k, v) in insert {
|
||||
transaction.set(columns::AUX, k, v);
|
||||
@@ -418,7 +436,8 @@ impl<Block> AuxStore for LightStorage<Block>
|
||||
}
|
||||
|
||||
impl<Block> Storage<Block> for LightStorage<Block>
|
||||
where Block: BlockT,
|
||||
where
|
||||
Block: BlockT,
|
||||
{
|
||||
fn import_header(
|
||||
&self,
|
||||
@@ -447,19 +466,12 @@ impl<Block> Storage<Block> for LightStorage<Block>
|
||||
self.set_head_with_transaction(&mut transaction, parent_hash, (number, hash))?;
|
||||
}
|
||||
|
||||
utils::insert_hash_to_key_mapping(
|
||||
&mut transaction,
|
||||
columns::KEY_LOOKUP,
|
||||
number,
|
||||
hash,
|
||||
)?;
|
||||
utils::insert_hash_to_key_mapping(&mut transaction, columns::KEY_LOOKUP, number, hash)?;
|
||||
transaction.set_from_vec(columns::HEADER, &lookup_key, header.encode());
|
||||
|
||||
let header_metadata = CachedHeaderMetadata::from(&header);
|
||||
self.header_metadata_cache.insert_header_metadata(
|
||||
header.hash().clone(),
|
||||
header_metadata,
|
||||
);
|
||||
self.header_metadata_cache
|
||||
.insert_header_metadata(header.hash().clone(), header_metadata);
|
||||
|
||||
let is_genesis = number.is_zero();
|
||||
if is_genesis {
|
||||
@@ -474,25 +486,28 @@ impl<Block> Storage<Block> for LightStorage<Block>
|
||||
};
|
||||
|
||||
if finalized {
|
||||
self.note_finalized(
|
||||
&mut transaction,
|
||||
&header,
|
||||
hash,
|
||||
)?;
|
||||
self.note_finalized(&mut transaction, &header, hash)?;
|
||||
}
|
||||
|
||||
// update changes trie configuration cache
|
||||
if !cache_at.contains_key(&well_known_cache_keys::CHANGES_TRIE_CONFIG) {
|
||||
if let Some(new_configuration) = crate::changes_tries_storage::extract_new_configuration(&header) {
|
||||
cache_at.insert(well_known_cache_keys::CHANGES_TRIE_CONFIG, new_configuration.encode());
|
||||
if let Some(new_configuration) =
|
||||
crate::changes_tries_storage::extract_new_configuration(&header)
|
||||
{
|
||||
cache_at
|
||||
.insert(well_known_cache_keys::CHANGES_TRIE_CONFIG, new_configuration.encode());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut cache = self.cache.0.write();
|
||||
let cache_ops = cache.transaction(&mut transaction)
|
||||
let cache_ops = cache
|
||||
.transaction(&mut transaction)
|
||||
.on_block_insert(
|
||||
ComplexBlockId::new(*header.parent_hash(), if number.is_zero() { Zero::zero() } else { number - One::one() }),
|
||||
ComplexBlockId::new(
|
||||
*header.parent_hash(),
|
||||
if number.is_zero() { Zero::zero() } else { number - One::one() },
|
||||
),
|
||||
ComplexBlockId::new(hash, number),
|
||||
cache_at,
|
||||
if finalized { CacheEntryType::Final } else { CacheEntryType::NonFinal },
|
||||
@@ -502,9 +517,10 @@ impl<Block> Storage<Block> for LightStorage<Block>
|
||||
debug!("Light DB Commit {:?} ({})", hash, number);
|
||||
|
||||
self.db.commit(transaction)?;
|
||||
cache.commit(cache_ops)
|
||||
.expect("only fails if cache with given name isn't loaded yet;\
|
||||
cache is already loaded because there are cache_ops; qed");
|
||||
cache.commit(cache_ops).expect(
|
||||
"only fails if cache with given name isn't loaded yet;\
|
||||
cache is already loaded because there are cache_ops; qed",
|
||||
);
|
||||
}
|
||||
|
||||
self.update_meta(hash, number, leaf_state.is_best(), finalized);
|
||||
@@ -518,7 +534,11 @@ impl<Block> Storage<Block> for LightStorage<Block>
|
||||
let number = header.number();
|
||||
|
||||
let mut transaction = Transaction::new();
|
||||
self.set_head_with_transaction(&mut transaction, hash.clone(), (number.clone(), hash.clone()))?;
|
||||
self.set_head_with_transaction(
|
||||
&mut transaction,
|
||||
hash.clone(),
|
||||
(number.clone(), hash.clone()),
|
||||
)?;
|
||||
self.db.commit(transaction)?;
|
||||
self.update_meta(hash, header.number().clone(), true, false);
|
||||
|
||||
@@ -536,17 +556,22 @@ impl<Block> Storage<Block> for LightStorage<Block>
|
||||
self.note_finalized(&mut transaction, &header, hash.clone())?;
|
||||
{
|
||||
let mut cache = self.cache.0.write();
|
||||
let cache_ops = cache.transaction(&mut transaction)
|
||||
let cache_ops = cache
|
||||
.transaction(&mut transaction)
|
||||
.on_block_finalize(
|
||||
ComplexBlockId::new(*header.parent_hash(), if number.is_zero() { Zero::zero() } else { number - One::one() }),
|
||||
ComplexBlockId::new(hash, number)
|
||||
ComplexBlockId::new(
|
||||
*header.parent_hash(),
|
||||
if number.is_zero() { Zero::zero() } else { number - One::one() },
|
||||
),
|
||||
ComplexBlockId::new(hash, number),
|
||||
)?
|
||||
.into_ops();
|
||||
|
||||
self.db.commit(transaction)?;
|
||||
cache.commit(cache_ops)
|
||||
.expect("only fails if cache with given name isn't loaded yet;\
|
||||
cache is already loaded because there are cache_ops; qed");
|
||||
cache.commit(cache_ops).expect(
|
||||
"only fails if cache with given name isn't loaded yet;\
|
||||
cache is already loaded because there are cache_ops; qed",
|
||||
);
|
||||
}
|
||||
self.update_meta(hash, header.number().clone(), false, true);
|
||||
|
||||
@@ -566,7 +591,7 @@ impl<Block> Storage<Block> for LightStorage<Block>
|
||||
|
||||
#[cfg(not(target_os = "unknown"))]
|
||||
fn usage_info(&self) -> Option<UsageInfo> {
|
||||
use sc_client_api::{MemoryInfo, IoInfo, MemorySize};
|
||||
use sc_client_api::{IoInfo, MemoryInfo, MemorySize};
|
||||
|
||||
// TODO: reimplement IO stats
|
||||
let database_cache = MemorySize::from_bytes(0);
|
||||
@@ -591,7 +616,7 @@ impl<Block> Storage<Block> for LightStorage<Block>
|
||||
state_reads_cache: 0,
|
||||
state_writes_cache: 0,
|
||||
state_writes_nodes: 0,
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -602,7 +627,8 @@ impl<Block> Storage<Block> for LightStorage<Block>
|
||||
}
|
||||
|
||||
impl<Block> ProvideChtRoots<Block> for LightStorage<Block>
|
||||
where Block: BlockT,
|
||||
where
|
||||
Block: BlockT,
|
||||
{
|
||||
fn header_cht_root(
|
||||
&self,
|
||||
@@ -630,12 +656,14 @@ fn cht_key<N: TryInto<u32>>(cht_type: u8, block: N) -> ClientResult<[u8; 5]> {
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use sc_client_api::cht;
|
||||
use sp_core::ChangesTrieConfiguration;
|
||||
use sp_runtime::generic::{DigestItem, ChangesTrieSignal};
|
||||
use sp_runtime::testing::{H256 as Hash, Header, Block as RawBlock, ExtrinsicWrapper};
|
||||
use sp_blockchain::{lowest_common_ancestor, tree_route};
|
||||
use super::*;
|
||||
use sc_client_api::cht;
|
||||
use sp_blockchain::{lowest_common_ancestor, tree_route};
|
||||
use sp_core::ChangesTrieConfiguration;
|
||||
use sp_runtime::{
|
||||
generic::{ChangesTrieSignal, DigestItem},
|
||||
testing::{Block as RawBlock, ExtrinsicWrapper, Header, H256 as Hash},
|
||||
};
|
||||
|
||||
type Block = RawBlock<ExtrinsicWrapper<u32>>;
|
||||
type AuthorityId = sp_core::ed25519::Public;
|
||||
@@ -652,7 +680,10 @@ pub(crate) mod tests {
|
||||
|
||||
fn header_with_changes_trie(parent: &Hash, number: u64) -> Header {
|
||||
let mut header = default_header(parent, number);
|
||||
header.digest.logs.push(DigestItem::ChangesTrieRoot([(number % 256) as u8; 32].into()));
|
||||
header
|
||||
.digest
|
||||
.logs
|
||||
.push(DigestItem::ChangesTrieRoot([(number % 256) as u8; 32].into()));
|
||||
header
|
||||
}
|
||||
|
||||
@@ -698,7 +729,8 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn returns_known_header() {
|
||||
let db = LightStorage::new_test();
|
||||
let known_hash = insert_block(&db, HashMap::new(), || 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);
|
||||
@@ -714,7 +746,8 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn returns_info() {
|
||||
let db = LightStorage::new_test();
|
||||
let genesis_hash = insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0));
|
||||
let genesis_hash =
|
||||
insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0));
|
||||
let info = db.info();
|
||||
assert_eq!(info.best_hash, genesis_hash);
|
||||
assert_eq!(info.best_number, 0);
|
||||
@@ -729,17 +762,22 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn returns_block_status() {
|
||||
let db = LightStorage::new_test();
|
||||
let genesis_hash = insert_block(&db, HashMap::new(), || 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);
|
||||
assert_eq!(
|
||||
db.status(BlockId::Hash(Hash::from_low_u64_be(1))).unwrap(),
|
||||
BlockStatus::Unknown
|
||||
);
|
||||
assert_eq!(db.status(BlockId::Number(1)).unwrap(), BlockStatus::Unknown);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_block_hash() {
|
||||
let db = LightStorage::new_test();
|
||||
let genesis_hash = insert_block(&db, HashMap::new(), || 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);
|
||||
}
|
||||
@@ -749,7 +787,8 @@ pub(crate) mod tests {
|
||||
let raw_db = Arc::new(sp_database::MemDb::default());
|
||||
let db = LightStorage::from_kvdb(raw_db.clone()).unwrap();
|
||||
|
||||
let genesis_hash = insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0));
|
||||
let genesis_hash =
|
||||
insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0));
|
||||
assert_eq!(raw_db.count(columns::HEADER), 1);
|
||||
assert_eq!(raw_db.count(columns::KEY_LOOKUP), 2);
|
||||
|
||||
@@ -760,43 +799,41 @@ pub(crate) mod tests {
|
||||
|
||||
#[test]
|
||||
fn finalized_ancient_headers_are_replaced_with_cht() {
|
||||
fn insert_headers<F: Fn(&Hash, u64) -> Header>(header_producer: F) ->
|
||||
(Arc<sp_database::MemDb>, LightStorage<Block>)
|
||||
{
|
||||
fn insert_headers<F: Fn(&Hash, u64) -> Header>(
|
||||
header_producer: F,
|
||||
) -> (Arc<sp_database::MemDb>, LightStorage<Block>) {
|
||||
let raw_db = Arc::new(sp_database::MemDb::default());
|
||||
let db = LightStorage::from_kvdb(raw_db.clone()).unwrap();
|
||||
let cht_size: u64 = cht::size();
|
||||
let ucht_size: usize = cht_size as _;
|
||||
|
||||
// insert genesis block header (never pruned)
|
||||
let mut prev_hash = insert_final_block(&db, HashMap::new(), || 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, HashMap::new(), || header_producer(&prev_hash, 1 + number));
|
||||
prev_hash =
|
||||
insert_block(&db, HashMap::new(), || header_producer(&prev_hash, 1 + number));
|
||||
}
|
||||
assert_eq!(raw_db.count(columns::HEADER), 1 + ucht_size);
|
||||
assert_eq!(raw_db.count(columns::CHT), 0);
|
||||
|
||||
// insert next SIZE blocks && ensure that nothing is pruned
|
||||
for number in 0..(cht_size as _) {
|
||||
prev_hash = insert_block(
|
||||
&db,
|
||||
HashMap::new(),
|
||||
|| 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!(raw_db.count(columns::HEADER), 1 + ucht_size + ucht_size);
|
||||
assert_eq!(raw_db.count(columns::CHT), 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,
|
||||
HashMap::new(),
|
||||
|| 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!(raw_db.count(columns::HEADER), 2 + ucht_size + ucht_size);
|
||||
assert_eq!(raw_db.count(columns::CHT), 0);
|
||||
|
||||
@@ -839,7 +876,10 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn get_cht_fails_for_non_existent_cht() {
|
||||
let cht_size: u64 = cht::size();
|
||||
assert!(LightStorage::<Block>::new_test().header_cht_root(cht_size, cht_size / 2).unwrap().is_none());
|
||||
assert!(LightStorage::<Block>::new_test()
|
||||
.header_cht_root(cht_size, cht_size / 2)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -847,26 +887,41 @@ 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, HashMap::new(), || header_with_changes_trie(&Default::default(), 0));
|
||||
let mut prev_hash = insert_final_block(&db, HashMap::new(), || {
|
||||
header_with_changes_trie(&Default::default(), 0)
|
||||
});
|
||||
let cht_size: u64 = cht::size();
|
||||
let ucht_size: usize = cht_size as _;
|
||||
for i in 1..1 + ucht_size + ucht_size + 1 {
|
||||
prev_hash = insert_block(&db, HashMap::new(), || 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();
|
||||
}
|
||||
|
||||
let cht_root_1 = db.header_cht_root(cht_size, cht::start_number(cht_size, 0)).unwrap().unwrap();
|
||||
let cht_root_2 = db.header_cht_root(cht_size, cht::start_number(cht_size, 0) + cht_size / 2).unwrap().unwrap();
|
||||
let cht_root_3 = db.header_cht_root(cht_size, cht::end_number(cht_size, 0)).unwrap().unwrap();
|
||||
let cht_root_1 =
|
||||
db.header_cht_root(cht_size, cht::start_number(cht_size, 0)).unwrap().unwrap();
|
||||
let cht_root_2 = db
|
||||
.header_cht_root(cht_size, cht::start_number(cht_size, 0) + cht_size / 2)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let cht_root_3 =
|
||||
db.header_cht_root(cht_size, cht::end_number(cht_size, 0)).unwrap().unwrap();
|
||||
assert_eq!(cht_root_1, cht_root_2);
|
||||
assert_eq!(cht_root_2, cht_root_3);
|
||||
|
||||
let cht_root_1 = db.changes_trie_cht_root(cht_size, cht::start_number(cht_size, 0)).unwrap().unwrap();
|
||||
let cht_root_2 = db.changes_trie_cht_root(
|
||||
cht_size,
|
||||
cht::start_number(cht_size, 0) + cht_size / 2,
|
||||
).unwrap().unwrap();
|
||||
let cht_root_3 = db.changes_trie_cht_root(cht_size, cht::end_number(cht_size, 0)).unwrap().unwrap();
|
||||
let cht_root_1 = db
|
||||
.changes_trie_cht_root(cht_size, cht::start_number(cht_size, 0))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let cht_root_2 = db
|
||||
.changes_trie_cht_root(cht_size, cht::start_number(cht_size, 0) + cht_size / 2)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let cht_root_3 = db
|
||||
.changes_trie_cht_root(cht_size, cht::end_number(cht_size, 0))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(cht_root_1, cht_root_2);
|
||||
assert_eq!(cht_root_2, cht_root_3);
|
||||
}
|
||||
@@ -882,15 +937,23 @@ pub(crate) mod tests {
|
||||
let a3 = insert_block(&db, HashMap::new(), || default_header(&a2, 3));
|
||||
|
||||
// fork from genesis: 2 prong.
|
||||
let b1 = insert_block(&db, HashMap::new(), || header_with_extrinsics_root(&block0, 1, Hash::from([1; 32])));
|
||||
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 = tree_route(&db, a3, b2).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, block0);
|
||||
assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2, a1]);
|
||||
assert_eq!(tree_route.enacted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![b1, b2]);
|
||||
assert_eq!(
|
||||
tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(),
|
||||
vec![a3, a2, a1]
|
||||
);
|
||||
assert_eq!(
|
||||
tree_route.enacted().iter().map(|r| r.hash).collect::<Vec<_>>(),
|
||||
vec![b1, b2]
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -898,14 +961,20 @@ pub(crate) mod tests {
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, a1);
|
||||
assert!(tree_route.retracted().is_empty());
|
||||
assert_eq!(tree_route.enacted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a2, a3]);
|
||||
assert_eq!(
|
||||
tree_route.enacted().iter().map(|r| r.hash).collect::<Vec<_>>(),
|
||||
vec![a2, a3]
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let tree_route = tree_route(&db, a3, a1).unwrap();
|
||||
|
||||
assert_eq!(tree_route.common_block().hash, a1);
|
||||
assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2]);
|
||||
assert_eq!(
|
||||
tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(),
|
||||
vec![a3, a2]
|
||||
);
|
||||
assert!(tree_route.enacted().is_empty());
|
||||
}
|
||||
|
||||
@@ -929,7 +998,9 @@ pub(crate) mod tests {
|
||||
let a3 = insert_block(&db, HashMap::new(), || default_header(&a2, 3));
|
||||
|
||||
// fork from genesis: 2 prong.
|
||||
let b1 = insert_block(&db, HashMap::new(), || header_with_extrinsics_root(&block0, 1, Hash::from([1; 32])));
|
||||
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));
|
||||
|
||||
{
|
||||
@@ -979,7 +1050,11 @@ pub(crate) mod tests {
|
||||
fn authorities_are_cached() {
|
||||
let db = LightStorage::new_test();
|
||||
|
||||
fn run_checks(db: &LightStorage<Block>, max: u64, checks: &[(u64, Option<Vec<AuthorityId>>)]) {
|
||||
fn run_checks(
|
||||
db: &LightStorage<Block>,
|
||||
max: u64,
|
||||
checks: &[(u64, Option<Vec<AuthorityId>>)],
|
||||
) {
|
||||
for (at, expected) in checks.iter().take_while(|(at, _)| *at <= max) {
|
||||
let actual = authorities(db.cache(), BlockId::Number(*at));
|
||||
assert_eq!(*expected, actual);
|
||||
@@ -990,14 +1065,21 @@ pub(crate) mod tests {
|
||||
HashMap::new()
|
||||
}
|
||||
|
||||
fn make_authorities(authorities: Vec<AuthorityId>) -> HashMap<well_known_cache_keys::Id, Vec<u8>> {
|
||||
fn make_authorities(
|
||||
authorities: Vec<AuthorityId>,
|
||||
) -> HashMap<well_known_cache_keys::Id, Vec<u8>> {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(well_known_cache_keys::AUTHORITIES, authorities.encode());
|
||||
map
|
||||
}
|
||||
|
||||
fn authorities(cache: &dyn BlockchainCache<Block>, at: BlockId<Block>) -> Option<Vec<AuthorityId>> {
|
||||
cache.get_at(&well_known_cache_keys::AUTHORITIES, &at).unwrap_or(None)
|
||||
fn authorities(
|
||||
cache: &dyn BlockchainCache<Block>,
|
||||
at: BlockId<Block>,
|
||||
) -> Option<Vec<AuthorityId>> {
|
||||
cache
|
||||
.get_at(&well_known_cache_keys::AUTHORITIES, &at)
|
||||
.unwrap_or(None)
|
||||
.and_then(|(_, _, val)| Decode::decode(&mut &val[..]).ok())
|
||||
}
|
||||
|
||||
@@ -1021,17 +1103,27 @@ pub(crate) mod tests {
|
||||
(6, Some(vec![auth1(), auth2()])),
|
||||
];
|
||||
|
||||
let hash0 = insert_final_block(&db, same_authorities(), || 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, same_authorities(), || default_header(&hash0, 1));
|
||||
run_checks(&db, 1, &checks);
|
||||
let hash2 = insert_final_block(&db, make_authorities(vec![auth1()]), || 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, make_authorities(vec![auth1()]), || 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, make_authorities(vec![auth1(), auth2()]), || 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, make_authorities(vec![auth1(), auth2()]), || 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, same_authorities(), || default_header(&hash5, 6));
|
||||
run_checks(&db, 6, &checks);
|
||||
@@ -1043,9 +1135,14 @@ 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, make_authorities(vec![auth1()]), || default_header(&hash2, 3));
|
||||
let hash2_1 = insert_non_best_block(&db, make_authorities(vec![auth1()]), || {
|
||||
default_header(&hash2, 3)
|
||||
});
|
||||
assert_eq!(None, 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));
|
||||
let hash2_2 =
|
||||
insert_non_best_block(&db, make_authorities(vec![auth1(), auth2()]), || {
|
||||
default_header(&hash2_1, 4)
|
||||
});
|
||||
assert_eq!(None, authorities(db.cache(), BlockId::Hash(hash2_2)));
|
||||
}
|
||||
|
||||
@@ -1056,51 +1153,41 @@ pub(crate) mod tests {
|
||||
// \> B6_1_1(5)
|
||||
// \> B6_1_2(6) -> B6_1_3(7)
|
||||
|
||||
let hash7 = insert_block(&db, make_authorities(vec![auth3()]), || default_header(&hash6, 7));
|
||||
assert_eq!(
|
||||
authorities(db.cache(), BlockId::Hash(hash6)),
|
||||
Some(vec![auth1(), auth2()]),
|
||||
);
|
||||
let hash7 =
|
||||
insert_block(&db, make_authorities(vec![auth3()]), || default_header(&hash6, 7));
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6)), Some(vec![auth1(), auth2()]),);
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash7)), Some(vec![auth3()]));
|
||||
let hash8 = insert_block(&db, make_authorities(vec![auth3()]), || default_header(&hash7, 8));
|
||||
assert_eq!(
|
||||
authorities(db.cache(), BlockId::Hash(hash6)),
|
||||
Some(vec![auth1(), auth2()]),
|
||||
);
|
||||
let hash8 =
|
||||
insert_block(&db, make_authorities(vec![auth3()]), || default_header(&hash7, 8));
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6)), Some(vec![auth1(), auth2()]),);
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash7)), Some(vec![auth3()]));
|
||||
assert_eq!(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!(
|
||||
authorities(db.cache(), BlockId::Hash(hash6)),
|
||||
Some(vec![auth1(), auth2()]),
|
||||
);
|
||||
let hash6_1 =
|
||||
insert_block(&db, make_authorities(vec![auth4()]), || default_header(&hash6, 7));
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6)), Some(vec![auth1(), auth2()]),);
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash7)), Some(vec![auth3()]));
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash8)), Some(vec![auth3()]));
|
||||
assert_eq!(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!(
|
||||
authorities(db.cache(), BlockId::Hash(hash6)),
|
||||
Some(vec![auth1(), auth2()]),
|
||||
);
|
||||
let hash6_1_1 = insert_non_best_block(&db, make_authorities(vec![auth5()]), || {
|
||||
default_header(&hash6_1, 8)
|
||||
});
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6)), Some(vec![auth1(), auth2()]),);
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash7)), Some(vec![auth3()]));
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash8)), Some(vec![auth3()]));
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6_1)), Some(vec![auth4()]));
|
||||
assert_eq!(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!(
|
||||
authorities(db.cache(), BlockId::Hash(hash6)),
|
||||
Some(vec![auth1(), auth2()]),
|
||||
);
|
||||
let hash6_1_2 = insert_non_best_block(&db, make_authorities(vec![auth6()]), || {
|
||||
default_header(&hash6_1, 8)
|
||||
});
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6)), Some(vec![auth1(), auth2()]),);
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash7)), Some(vec![auth3()]));
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash8)), Some(vec![auth3()]));
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6_1)), Some(vec![auth4()]));
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6_1_1)), Some(vec![auth5()]));
|
||||
assert_eq!(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!(
|
||||
authorities(db.cache(), BlockId::Hash(hash6)),
|
||||
Some(vec![auth1(), auth2()]),
|
||||
);
|
||||
let hash6_2 =
|
||||
insert_block(&db, make_authorities(vec![auth4()]), || default_header(&hash6_1, 8));
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6)), Some(vec![auth1(), auth2()]),);
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash7)), Some(vec![auth3()]));
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash8)), Some(vec![auth3()]));
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6_1)), Some(vec![auth4()]));
|
||||
@@ -1114,10 +1201,7 @@ pub(crate) mod tests {
|
||||
{
|
||||
// finalize block hash6_1
|
||||
db.finalize_header(BlockId::Hash(hash6_1)).unwrap();
|
||||
assert_eq!(
|
||||
authorities(db.cache(), BlockId::Hash(hash6)),
|
||||
Some(vec![auth1(), auth2()]),
|
||||
);
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6)), Some(vec![auth1(), auth2()]),);
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash7)), None);
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash8)), None);
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6_1)), Some(vec![auth4()]));
|
||||
@@ -1126,10 +1210,7 @@ pub(crate) mod tests {
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6_2)), Some(vec![auth4()]));
|
||||
// finalize block hash6_2
|
||||
db.finalize_header(BlockId::Hash(hash6_2)).unwrap();
|
||||
assert_eq!(
|
||||
authorities(db.cache(), BlockId::Hash(hash6)),
|
||||
Some(vec![auth1(), auth2()]),
|
||||
);
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6)), Some(vec![auth1(), auth2()]),);
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash7)), None);
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash8)), None);
|
||||
assert_eq!(authorities(db.cache(), BlockId::Hash(hash6_1)), Some(vec![auth4()]));
|
||||
@@ -1142,7 +1223,8 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn database_is_reopened() {
|
||||
let db = LightStorage::new_test();
|
||||
let hash0 = insert_final_block(&db, HashMap::new(), || default_header(&Default::default(), 0));
|
||||
let hash0 =
|
||||
insert_final_block(&db, HashMap::new(), || default_header(&Default::default(), 0));
|
||||
assert_eq!(db.info().best_hash, hash0);
|
||||
assert_eq!(db.header(BlockId::Hash(hash0)).unwrap().unwrap().hash(), hash0);
|
||||
|
||||
@@ -1157,7 +1239,8 @@ pub(crate) mod tests {
|
||||
let db = LightStorage::<Block>::new_test();
|
||||
|
||||
// insert aux1 + aux2 using direct store access
|
||||
db.insert_aux(&[(&[1][..], &[101][..]), (&[2][..], &[102][..])], ::std::iter::empty()).unwrap();
|
||||
db.insert_aux(&[(&[1][..], &[101][..]), (&[2][..], &[102][..])], ::std::iter::empty())
|
||||
.unwrap();
|
||||
|
||||
// check aux values
|
||||
assert_eq!(db.get_aux(&[1]).unwrap(), Some(vec![101]));
|
||||
@@ -1165,10 +1248,13 @@ 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), HashMap::new(), NewBlockState::Best, vec![
|
||||
(vec![3], Some(vec![103])),
|
||||
(vec![1], None),
|
||||
]).unwrap();
|
||||
db.import_header(
|
||||
default_header(&Default::default(), 0),
|
||||
HashMap::new(),
|
||||
NewBlockState::Best,
|
||||
vec![(vec![3], Some(vec![103])), (vec![1], None)],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// check aux values
|
||||
assert_eq!(db.get_aux(&[1]).unwrap(), None);
|
||||
@@ -1208,7 +1294,8 @@ pub(crate) mod tests {
|
||||
};
|
||||
|
||||
// restart && check that after restart value is read from the cache
|
||||
let db = LightStorage::<Block>::from_kvdb(storage as Arc<_>).expect("failed to create test-db");
|
||||
let db =
|
||||
LightStorage::<Block>::from_kvdb(storage as Arc<_>).expect("failed to create test-db");
|
||||
assert_eq!(
|
||||
db.cache().get_at(b"test", &BlockId::Number(0)).unwrap(),
|
||||
Some(((0, genesis_hash.unwrap()), None, vec![42])),
|
||||
@@ -1224,7 +1311,9 @@ pub(crate) mod tests {
|
||||
// insert block#0 && block#1 (no value for cache is provided)
|
||||
let hash0 = insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0));
|
||||
assert_eq!(
|
||||
db.cache().get_at(&well_known_cache_keys::CHANGES_TRIE_CONFIG, &BlockId::Number(0)).unwrap()
|
||||
db.cache()
|
||||
.get_at(&well_known_cache_keys::CHANGES_TRIE_CONFIG, &BlockId::Number(0))
|
||||
.unwrap()
|
||||
.map(|(_, _, v)| ChangesTrieConfiguration::decode(&mut &v[..]).unwrap()),
|
||||
None,
|
||||
);
|
||||
@@ -1232,13 +1321,15 @@ pub(crate) mod tests {
|
||||
// insert configuration at block#1 (starts from block#2)
|
||||
insert_block(&db, HashMap::new(), || {
|
||||
let mut header = default_header(&hash0, 1);
|
||||
header.digest_mut().push(
|
||||
DigestItem::ChangesTrieSignal(ChangesTrieSignal::NewConfiguration(new_config.clone()))
|
||||
);
|
||||
header.digest_mut().push(DigestItem::ChangesTrieSignal(
|
||||
ChangesTrieSignal::NewConfiguration(new_config.clone()),
|
||||
));
|
||||
header
|
||||
});
|
||||
assert_eq!(
|
||||
db.cache().get_at(&well_known_cache_keys::CHANGES_TRIE_CONFIG, &BlockId::Number(1)).unwrap()
|
||||
db.cache()
|
||||
.get_at(&well_known_cache_keys::CHANGES_TRIE_CONFIG, &BlockId::Number(1))
|
||||
.unwrap()
|
||||
.map(|(_, _, v)| Option::<ChangesTrieConfiguration>::decode(&mut &v[..]).unwrap()),
|
||||
Some(new_config),
|
||||
);
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use crate::{columns, Database, DbHash, Transaction};
|
||||
use parking_lot::Mutex;
|
||||
use log::error;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
/// Offchain local storage
|
||||
#[derive(Clone)]
|
||||
@@ -33,8 +33,7 @@ pub struct LocalStorage {
|
||||
|
||||
impl std::fmt::Debug for LocalStorage {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
fmt.debug_struct("LocalStorage")
|
||||
.finish()
|
||||
fmt.debug_struct("LocalStorage").finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,10 +48,7 @@ impl LocalStorage {
|
||||
|
||||
/// Create offchain local storage with given `KeyValueDB` backend.
|
||||
pub fn new(db: Arc<dyn Database<DbHash>>) -> Self {
|
||||
Self {
|
||||
db,
|
||||
locks: Default::default(),
|
||||
}
|
||||
Self { db, locks: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,11 +114,7 @@ impl sp_core::offchain::OffchainStorage for LocalStorage {
|
||||
|
||||
/// Concatenate the prefix and key to create an offchain key in the db.
|
||||
pub(crate) fn concatenate_prefix_and_key(prefix: &[u8], key: &[u8]) -> Vec<u8> {
|
||||
prefix
|
||||
.iter()
|
||||
.chain(key.into_iter())
|
||||
.cloned()
|
||||
.collect()
|
||||
prefix.iter().chain(key.into_iter()).cloned().collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -155,5 +147,4 @@ mod tests {
|
||||
assert_eq!(storage.get(prefix, key), Some(b"asd".to_vec()));
|
||||
assert!(storage.locks.lock().is_empty(), "Locks map should be empty!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,27 +15,29 @@
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
use crate::{
|
||||
columns,
|
||||
utils::{DatabaseType, NUM_COLUMNS},
|
||||
};
|
||||
/// A `Database` adapter for parity-db.
|
||||
|
||||
use sp_database::{Database, Change, ColumnId, Transaction, error::DatabaseError};
|
||||
use crate::utils::{DatabaseType, NUM_COLUMNS};
|
||||
use crate::columns;
|
||||
use sp_database::{error::DatabaseError, Change, ColumnId, Database, Transaction};
|
||||
|
||||
struct DbAdapter(parity_db::Db);
|
||||
|
||||
fn handle_err<T>(result: parity_db::Result<T>) -> T {
|
||||
match result {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Err(e) => {
|
||||
panic!("Critical database error: {:?}", e);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap parity-db database into a trait object that implements `sp_database::Database`
|
||||
pub fn open<H: Clone + AsRef<[u8]>>(path: &std::path::Path, db_type: DatabaseType)
|
||||
-> parity_db::Result<std::sync::Arc<dyn Database<H>>>
|
||||
{
|
||||
pub fn open<H: Clone + AsRef<[u8]>>(
|
||||
path: &std::path::Path,
|
||||
db_type: DatabaseType,
|
||||
) -> parity_db::Result<std::sync::Arc<dyn Database<H>>> {
|
||||
let mut config = parity_db::Options::with_columns(path, NUM_COLUMNS as u8);
|
||||
config.sync = true; // Flush each commit
|
||||
if db_type == DatabaseType::Full {
|
||||
@@ -50,13 +52,11 @@ pub fn open<H: Clone + AsRef<[u8]>>(path: &std::path::Path, db_type: DatabaseTyp
|
||||
|
||||
impl<H: Clone + AsRef<[u8]>> Database<H> for DbAdapter {
|
||||
fn commit(&self, transaction: Transaction<H>) -> Result<(), DatabaseError> {
|
||||
handle_err(self.0.commit(transaction.0.into_iter().map(|change|
|
||||
match change {
|
||||
Change::Set(col, key, value) => (col as u8, key, Some(value)),
|
||||
Change::Remove(col, key) => (col as u8, key, None),
|
||||
_ => unimplemented!(),
|
||||
}))
|
||||
);
|
||||
handle_err(self.0.commit(transaction.0.into_iter().map(|change| match change {
|
||||
Change::Set(col, key, value) => (col as u8, key, Some(value)),
|
||||
Change::Remove(col, key) => (col as u8, key, None),
|
||||
_ => unimplemented!(),
|
||||
})));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -65,7 +65,10 @@ impl StateUsageStats {
|
||||
|
||||
/// Tally one key read.
|
||||
pub fn tally_key_read(&self, key: &[u8], val: Option<&Vec<u8>>, cache: bool) {
|
||||
self.tally_read(key.len() as u64 + val.as_ref().map(|x| x.len() as u64).unwrap_or(0), cache);
|
||||
self.tally_read(
|
||||
key.len() as u64 + val.as_ref().map(|x| x.len() as u64).unwrap_or(0),
|
||||
cache,
|
||||
);
|
||||
}
|
||||
|
||||
/// Tally one child key read.
|
||||
@@ -103,9 +106,11 @@ impl StateUsageStats {
|
||||
self.reads.fetch_add(info.reads.ops, AtomicOrdering::Relaxed);
|
||||
self.bytes_read.fetch_add(info.reads.bytes, AtomicOrdering::Relaxed);
|
||||
self.writes_nodes.fetch_add(info.nodes_writes.ops, AtomicOrdering::Relaxed);
|
||||
self.bytes_written_nodes.fetch_add(info.nodes_writes.bytes, AtomicOrdering::Relaxed);
|
||||
self.bytes_written_nodes
|
||||
.fetch_add(info.nodes_writes.bytes, AtomicOrdering::Relaxed);
|
||||
self.removed_nodes.fetch_add(info.removed_nodes.ops, AtomicOrdering::Relaxed);
|
||||
self.bytes_removed_nodes.fetch_add(info.removed_nodes.bytes, AtomicOrdering::Relaxed);
|
||||
self.bytes_removed_nodes
|
||||
.fetch_add(info.removed_nodes.bytes, AtomicOrdering::Relaxed);
|
||||
self.reads_cache.fetch_add(info.cache_reads.ops, AtomicOrdering::Relaxed);
|
||||
self.bytes_read_cache.fetch_add(info.cache_reads.bytes, AtomicOrdering::Relaxed);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,14 +18,16 @@
|
||||
|
||||
//! Database upgrade logic.
|
||||
|
||||
use std::fs;
|
||||
use std::io::{Read, Write, ErrorKind};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{
|
||||
fs,
|
||||
io::{ErrorKind, Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
use crate::{columns, utils::DatabaseType};
|
||||
use kvdb_rocksdb::{Database, DatabaseConfig};
|
||||
use codec::{Decode, Encode};
|
||||
use kvdb_rocksdb::{Database, DatabaseConfig};
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
|
||||
/// Version file name.
|
||||
const VERSION_FILE_NAME: &'static str = "db_version";
|
||||
@@ -38,19 +40,28 @@ const V1_NUM_COLUMNS: u32 = 11;
|
||||
const V2_NUM_COLUMNS: u32 = 12;
|
||||
|
||||
/// Upgrade database to current version.
|
||||
pub fn upgrade_db<Block: BlockT>(db_path: &Path, db_type: DatabaseType) -> sp_blockchain::Result<()> {
|
||||
pub fn upgrade_db<Block: BlockT>(
|
||||
db_path: &Path,
|
||||
db_type: DatabaseType,
|
||||
) -> sp_blockchain::Result<()> {
|
||||
let is_empty = db_path.read_dir().map_or(true, |mut d| d.next().is_none());
|
||||
if !is_empty {
|
||||
let db_version = current_version(db_path)?;
|
||||
match db_version {
|
||||
0 => Err(sp_blockchain::Error::Backend(format!("Unsupported database version: {}", db_version)))?,
|
||||
0 => Err(sp_blockchain::Error::Backend(format!(
|
||||
"Unsupported database version: {}",
|
||||
db_version
|
||||
)))?,
|
||||
1 => {
|
||||
migrate_1_to_2::<Block>(db_path, db_type)?;
|
||||
migrate_2_to_3::<Block>(db_path, db_type)?
|
||||
},
|
||||
2 => migrate_2_to_3::<Block>(db_path, db_type)?,
|
||||
CURRENT_VERSION => (),
|
||||
_ => Err(sp_blockchain::Error::Backend(format!("Future database version: {}", db_version)))?,
|
||||
_ => Err(sp_blockchain::Error::Backend(format!(
|
||||
"Future database version: {}",
|
||||
db_version
|
||||
)))?,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,8 +71,12 @@ pub fn upgrade_db<Block: BlockT>(db_path: &Path, db_type: DatabaseType) -> sp_bl
|
||||
/// Migration from version1 to version2:
|
||||
/// 1) the number of columns has changed from 11 to 12;
|
||||
/// 2) transactions column is added;
|
||||
fn migrate_1_to_2<Block: BlockT>(db_path: &Path, _db_type: DatabaseType) -> sp_blockchain::Result<()> {
|
||||
let db_path = db_path.to_str()
|
||||
fn migrate_1_to_2<Block: BlockT>(
|
||||
db_path: &Path,
|
||||
_db_type: DatabaseType,
|
||||
) -> sp_blockchain::Result<()> {
|
||||
let db_path = db_path
|
||||
.to_str()
|
||||
.ok_or_else(|| sp_blockchain::Error::Backend("Invalid database path".into()))?;
|
||||
let db_cfg = DatabaseConfig::with_columns(V1_NUM_COLUMNS);
|
||||
let db = Database::open(&db_cfg, db_path).map_err(db_err)?;
|
||||
@@ -70,8 +85,12 @@ fn migrate_1_to_2<Block: BlockT>(db_path: &Path, _db_type: DatabaseType) -> sp_b
|
||||
|
||||
/// Migration from version2 to version3:
|
||||
/// - The format of the stored Justification changed to support multiple Justifications.
|
||||
fn migrate_2_to_3<Block: BlockT>(db_path: &Path, _db_type: DatabaseType) -> sp_blockchain::Result<()> {
|
||||
let db_path = db_path.to_str()
|
||||
fn migrate_2_to_3<Block: BlockT>(
|
||||
db_path: &Path,
|
||||
_db_type: DatabaseType,
|
||||
) -> sp_blockchain::Result<()> {
|
||||
let db_path = db_path
|
||||
.to_str()
|
||||
.ok_or_else(|| sp_blockchain::Error::Backend("Invalid database path".into()))?;
|
||||
let db_cfg = DatabaseConfig::with_columns(V2_NUM_COLUMNS);
|
||||
let db = Database::open(&db_cfg, db_path).map_err(db_err)?;
|
||||
@@ -137,10 +156,11 @@ fn version_file_path(path: &Path) -> PathBuf {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use sc_state_db::PruningMode;
|
||||
use crate::{DatabaseSettings, DatabaseSettingsSrc, KeepBlocks, TransactionStorageMode};
|
||||
use crate::tests::Block;
|
||||
use super::*;
|
||||
use crate::{
|
||||
tests::Block, DatabaseSettings, DatabaseSettingsSrc, KeepBlocks, TransactionStorageMode,
|
||||
};
|
||||
use sc_state_db::PruningMode;
|
||||
|
||||
fn create_db(db_path: &Path, version: Option<u32>) {
|
||||
if let Some(version) = version {
|
||||
@@ -151,14 +171,18 @@ mod tests {
|
||||
}
|
||||
|
||||
fn open_database(db_path: &Path) -> sp_blockchain::Result<()> {
|
||||
crate::utils::open_database::<Block>(&DatabaseSettings {
|
||||
state_cache_size: 0,
|
||||
state_cache_child_ratio: None,
|
||||
state_pruning: PruningMode::ArchiveAll,
|
||||
source: DatabaseSettingsSrc::RocksDb { path: db_path.to_owned(), cache_size: 128 },
|
||||
keep_blocks: KeepBlocks::All,
|
||||
transaction_storage: TransactionStorageMode::BlockBody,
|
||||
}, DatabaseType::Full).map(|_| ())
|
||||
crate::utils::open_database::<Block>(
|
||||
&DatabaseSettings {
|
||||
state_cache_size: 0,
|
||||
state_cache_child_ratio: None,
|
||||
state_pruning: PruningMode::ArchiveAll,
|
||||
source: DatabaseSettingsSrc::RocksDb { path: db_path.to_owned(), cache_size: 128 },
|
||||
keep_blocks: KeepBlocks::All,
|
||||
transaction_storage: TransactionStorageMode::BlockBody,
|
||||
},
|
||||
DatabaseType::Full,
|
||||
)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -19,24 +19,27 @@
|
||||
//! Db-based backend utility structures and functions, used by both
|
||||
//! full and light storages.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::convert::TryInto;
|
||||
use std::{convert::TryInto, sync::Arc};
|
||||
|
||||
use log::debug;
|
||||
|
||||
use crate::{Database, DatabaseSettings, DatabaseSettingsSrc, DbHash};
|
||||
use codec::Decode;
|
||||
use sp_trie::DBValue;
|
||||
use sp_database::Transaction;
|
||||
use sp_runtime::generic::BlockId;
|
||||
use sp_runtime::traits::{
|
||||
Block as BlockT, Header as HeaderT, Zero,
|
||||
UniqueSaturatedFrom, UniqueSaturatedInto,
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, Header as HeaderT, UniqueSaturatedFrom, UniqueSaturatedInto, Zero},
|
||||
};
|
||||
use crate::{DatabaseSettings, DatabaseSettingsSrc, Database, DbHash};
|
||||
use sp_trie::DBValue;
|
||||
|
||||
/// Number of columns in the db. Must be the same for both full && light dbs.
|
||||
/// Otherwise RocksDb will fail to open database && check its type.
|
||||
#[cfg(any(feature = "with-kvdb-rocksdb", feature = "with-parity-db", feature = "test-helpers", test))]
|
||||
#[cfg(any(
|
||||
feature = "with-kvdb-rocksdb",
|
||||
feature = "with-parity-db",
|
||||
feature = "test-helpers",
|
||||
test
|
||||
))]
|
||||
pub const NUM_COLUMNS: u32 = 12;
|
||||
/// Meta column. The set of keys in the column is shared by full && light storages.
|
||||
pub const COLUMN_META: u32 = 0;
|
||||
@@ -98,24 +101,17 @@ pub enum DatabaseType {
|
||||
/// In the current database schema, this kind of key is only used for
|
||||
/// lookups into an index, NOT for storing header data or others.
|
||||
pub fn number_index_key<N: TryInto<u32>>(n: N) -> sp_blockchain::Result<NumberIndexKey> {
|
||||
let n = n.try_into().map_err(|_|
|
||||
let n = n.try_into().map_err(|_| {
|
||||
sp_blockchain::Error::Backend("Block number cannot be converted to u32".into())
|
||||
)?;
|
||||
})?;
|
||||
|
||||
Ok([
|
||||
(n >> 24) as u8,
|
||||
((n >> 16) & 0xff) as u8,
|
||||
((n >> 8) & 0xff) as u8,
|
||||
(n & 0xff) as u8
|
||||
])
|
||||
Ok([(n >> 24) as u8, ((n >> 16) & 0xff) as u8, ((n >> 8) & 0xff) as u8, (n & 0xff) as u8])
|
||||
}
|
||||
|
||||
/// Convert number and hash into long lookup key for blocks that are
|
||||
/// not in the canonical chain.
|
||||
pub fn number_and_hash_to_lookup_key<N, H>(
|
||||
number: N,
|
||||
hash: H,
|
||||
) -> sp_blockchain::Result<Vec<u8>> where
|
||||
pub fn number_and_hash_to_lookup_key<N, H>(number: N, hash: H) -> sp_blockchain::Result<Vec<u8>>
|
||||
where
|
||||
N: TryInto<u32>,
|
||||
H: AsRef<[u8]>,
|
||||
{
|
||||
@@ -126,16 +122,15 @@ pub fn number_and_hash_to_lookup_key<N, H>(
|
||||
|
||||
/// Convert block lookup key into block number.
|
||||
/// all block lookup keys start with the block number.
|
||||
pub fn lookup_key_to_number<N>(key: &[u8]) -> sp_blockchain::Result<N> where
|
||||
N: From<u32>
|
||||
pub fn lookup_key_to_number<N>(key: &[u8]) -> sp_blockchain::Result<N>
|
||||
where
|
||||
N: From<u32>,
|
||||
{
|
||||
if key.len() < 4 {
|
||||
return Err(sp_blockchain::Error::Backend("Invalid block key".into()));
|
||||
return Err(sp_blockchain::Error::Backend("Invalid block key".into()))
|
||||
}
|
||||
Ok((key[0] as u32) << 24
|
||||
| (key[1] as u32) << 16
|
||||
| (key[2] as u32) << 8
|
||||
| (key[3] as u32)).map(Into::into)
|
||||
Ok((key[0] as u32) << 24 | (key[1] as u32) << 16 | (key[2] as u32) << 8 | (key[3] as u32))
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
/// Delete number to hash mapping in DB transaction.
|
||||
@@ -197,17 +192,15 @@ pub fn insert_hash_to_key_mapping<N: TryInto<u32>, H: AsRef<[u8]> + Clone>(
|
||||
pub fn block_id_to_lookup_key<Block>(
|
||||
db: &dyn Database<DbHash>,
|
||||
key_lookup_col: u32,
|
||||
id: BlockId<Block>
|
||||
) -> Result<Option<Vec<u8>>, sp_blockchain::Error> where
|
||||
id: BlockId<Block>,
|
||||
) -> Result<Option<Vec<u8>>, sp_blockchain::Error>
|
||||
where
|
||||
Block: BlockT,
|
||||
::sp_runtime::traits::NumberFor<Block>: UniqueSaturatedFrom<u64> + UniqueSaturatedInto<u64>,
|
||||
{
|
||||
Ok(match id {
|
||||
BlockId::Number(n) => db.get(
|
||||
key_lookup_col,
|
||||
number_index_key(n)?.as_ref(),
|
||||
),
|
||||
BlockId::Hash(h) => db.get(key_lookup_col, h.as_ref())
|
||||
BlockId::Number(n) => db.get(key_lookup_col, number_index_key(n)?.as_ref()),
|
||||
BlockId::Hash(h) => db.get(key_lookup_col, h.as_ref()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -218,9 +211,10 @@ pub fn open_database<Block: BlockT>(
|
||||
) -> sp_blockchain::Result<Arc<dyn Database<DbHash>>> {
|
||||
#[allow(unused)]
|
||||
fn db_open_error(feat: &'static str) -> sp_blockchain::Error {
|
||||
sp_blockchain::Error::Backend(
|
||||
format!("`{}` feature not enabled, database can not be opened", feat),
|
||||
)
|
||||
sp_blockchain::Error::Backend(format!(
|
||||
"`{}` feature not enabled, database can not be opened",
|
||||
feat
|
||||
))
|
||||
}
|
||||
|
||||
let db: Arc<dyn Database<DbHash>> = match &config.source {
|
||||
@@ -231,14 +225,16 @@ pub fn open_database<Block: BlockT>(
|
||||
|
||||
// and now open database assuming that it has the latest version
|
||||
let mut db_config = kvdb_rocksdb::DatabaseConfig::with_columns(NUM_COLUMNS);
|
||||
let path = path.to_str()
|
||||
let path = path
|
||||
.to_str()
|
||||
.ok_or_else(|| sp_blockchain::Error::Backend("Invalid database path".into()))?;
|
||||
|
||||
let mut memory_budget = std::collections::HashMap::new();
|
||||
match db_type {
|
||||
DatabaseType::Full => {
|
||||
let state_col_budget = (*cache_size as f64 * 0.9) as usize;
|
||||
let other_col_budget = (cache_size - state_col_budget) / (NUM_COLUMNS as usize - 1);
|
||||
let other_col_budget =
|
||||
(cache_size - state_col_budget) / (NUM_COLUMNS as usize - 1);
|
||||
|
||||
for i in 0..NUM_COLUMNS {
|
||||
if i == crate::columns::STATE {
|
||||
@@ -267,7 +263,7 @@ pub fn open_database<Block: BlockT>(
|
||||
path,
|
||||
col_budget,
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
db_config.memory_budget = memory_budget;
|
||||
|
||||
@@ -276,18 +272,12 @@ pub fn open_database<Block: BlockT>(
|
||||
sp_database::as_database(db)
|
||||
},
|
||||
#[cfg(not(any(feature = "with-kvdb-rocksdb", test)))]
|
||||
DatabaseSettingsSrc::RocksDb { .. } => {
|
||||
return Err(db_open_error("with-kvdb-rocksdb"));
|
||||
},
|
||||
DatabaseSettingsSrc::RocksDb { .. } => return Err(db_open_error("with-kvdb-rocksdb")),
|
||||
#[cfg(feature = "with-parity-db")]
|
||||
DatabaseSettingsSrc::ParityDb { path } => {
|
||||
crate::parity_db::open(&path, db_type)
|
||||
.map_err(|e| sp_blockchain::Error::Backend(format!("{}", e)))?
|
||||
},
|
||||
DatabaseSettingsSrc::ParityDb { path } => crate::parity_db::open(&path, db_type)
|
||||
.map_err(|e| sp_blockchain::Error::Backend(format!("{}", e)))?,
|
||||
#[cfg(not(feature = "with-parity-db"))]
|
||||
DatabaseSettingsSrc::ParityDb { .. } => {
|
||||
return Err(db_open_error("with-parity-db"))
|
||||
},
|
||||
DatabaseSettingsSrc::ParityDb { .. } => return Err(db_open_error("with-parity-db")),
|
||||
DatabaseSettingsSrc::Custom(db) => db.clone(),
|
||||
};
|
||||
|
||||
@@ -297,14 +287,19 @@ pub fn open_database<Block: BlockT>(
|
||||
}
|
||||
|
||||
/// Check database type.
|
||||
pub fn check_database_type(db: &dyn Database<DbHash>, db_type: DatabaseType) -> sp_blockchain::Result<()> {
|
||||
pub fn check_database_type(
|
||||
db: &dyn Database<DbHash>,
|
||||
db_type: DatabaseType,
|
||||
) -> sp_blockchain::Result<()> {
|
||||
match db.get(COLUMN_META, meta_keys::TYPE) {
|
||||
Some(stored_type) => {
|
||||
Some(stored_type) =>
|
||||
if db_type.as_str().as_bytes() != &*stored_type {
|
||||
return Err(sp_blockchain::Error::Backend(
|
||||
format!("Unexpected database type. Expected: {}", db_type.as_str())).into());
|
||||
}
|
||||
},
|
||||
return Err(sp_blockchain::Error::Backend(format!(
|
||||
"Unexpected database type. Expected: {}",
|
||||
db_type.as_str()
|
||||
))
|
||||
.into())
|
||||
},
|
||||
None => {
|
||||
let mut transaction = Transaction::new();
|
||||
transaction.set(COLUMN_META, meta_keys::TYPE, db_type.as_str().as_bytes());
|
||||
@@ -320,10 +315,10 @@ pub fn read_db<Block>(
|
||||
db: &dyn Database<DbHash>,
|
||||
col_index: u32,
|
||||
col: u32,
|
||||
id: BlockId<Block>
|
||||
id: BlockId<Block>,
|
||||
) -> sp_blockchain::Result<Option<DBValue>>
|
||||
where
|
||||
Block: BlockT,
|
||||
where
|
||||
Block: BlockT,
|
||||
{
|
||||
block_id_to_lookup_key(db, col_index, id).and_then(|key| match key {
|
||||
Some(key) => Ok(db.get(col, key.as_ref())),
|
||||
@@ -358,10 +353,8 @@ pub fn read_header<Block: BlockT>(
|
||||
match read_db(db, col_index, col, id)? {
|
||||
Some(header) => match Block::Header::decode(&mut &header[..]) {
|
||||
Ok(header) => Ok(Some(header)),
|
||||
Err(_) => return Err(
|
||||
sp_blockchain::Error::Backend("Error decoding header".into())
|
||||
),
|
||||
}
|
||||
Err(_) => return Err(sp_blockchain::Error::Backend("Error decoding header".into())),
|
||||
},
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
@@ -373,34 +366,35 @@ pub fn require_header<Block: BlockT>(
|
||||
col: u32,
|
||||
id: BlockId<Block>,
|
||||
) -> sp_blockchain::Result<Block::Header> {
|
||||
read_header(db, col_index, col, id)
|
||||
.and_then(|header| header.ok_or_else(||
|
||||
sp_blockchain::Error::UnknownBlock(format!("Require header: {}", id))
|
||||
))
|
||||
read_header(db, col_index, col, id).and_then(|header| {
|
||||
header.ok_or_else(|| sp_blockchain::Error::UnknownBlock(format!("Require header: {}", id)))
|
||||
})
|
||||
}
|
||||
|
||||
/// Read meta from the database.
|
||||
pub fn read_meta<Block>(db: &dyn Database<DbHash>, col_header: u32) -> Result<
|
||||
Meta<<<Block as BlockT>::Header as HeaderT>::Number, Block::Hash>,
|
||||
sp_blockchain::Error,
|
||||
>
|
||||
where
|
||||
Block: BlockT,
|
||||
pub fn read_meta<Block>(
|
||||
db: &dyn Database<DbHash>,
|
||||
col_header: u32,
|
||||
) -> Result<Meta<<<Block as BlockT>::Header as HeaderT>::Number, Block::Hash>, sp_blockchain::Error>
|
||||
where
|
||||
Block: BlockT,
|
||||
{
|
||||
let genesis_hash: Block::Hash = match read_genesis_hash(db)? {
|
||||
Some(genesis_hash) => genesis_hash,
|
||||
None => return Ok(Meta {
|
||||
best_hash: Default::default(),
|
||||
best_number: Zero::zero(),
|
||||
finalized_hash: Default::default(),
|
||||
finalized_number: Zero::zero(),
|
||||
genesis_hash: Default::default(),
|
||||
finalized_state: None,
|
||||
}),
|
||||
None =>
|
||||
return Ok(Meta {
|
||||
best_hash: Default::default(),
|
||||
best_number: Zero::zero(),
|
||||
finalized_hash: Default::default(),
|
||||
finalized_number: Zero::zero(),
|
||||
genesis_hash: Default::default(),
|
||||
finalized_state: None,
|
||||
}),
|
||||
};
|
||||
|
||||
let load_meta_block = |desc, key| -> Result<_, sp_blockchain::Error> {
|
||||
if let Some(Some(header)) = db.get(COLUMN_META, key)
|
||||
if let Some(Some(header)) = db
|
||||
.get(COLUMN_META, key)
|
||||
.and_then(|id| db.get(col_header, &id).map(|b| Block::Header::decode(&mut &b[..]).ok()))
|
||||
{
|
||||
let hash = header.hash();
|
||||
@@ -419,7 +413,8 @@ pub fn read_meta<Block>(db: &dyn Database<DbHash>, col_header: u32) -> Result<
|
||||
|
||||
let (best_hash, best_number) = load_meta_block("best", meta_keys::BEST_BLOCK)?;
|
||||
let (finalized_hash, finalized_number) = load_meta_block("final", meta_keys::FINALIZED_BLOCK)?;
|
||||
let (finalized_state_hash, finalized_state_number) = load_meta_block("final_state", meta_keys::FINALIZED_STATE)?;
|
||||
let (finalized_state_hash, finalized_state_number) =
|
||||
load_meta_block("final_state", meta_keys::FINALIZED_STATE)?;
|
||||
let finalized_state = if finalized_state_hash != Default::default() {
|
||||
Some((finalized_state_hash, finalized_state_number))
|
||||
} else {
|
||||
@@ -437,13 +432,14 @@ pub fn read_meta<Block>(db: &dyn Database<DbHash>, col_header: u32) -> Result<
|
||||
}
|
||||
|
||||
/// Read genesis hash from database.
|
||||
pub fn read_genesis_hash<Hash: Decode>(db: &dyn Database<DbHash>) -> sp_blockchain::Result<Option<Hash>> {
|
||||
pub fn read_genesis_hash<Hash: Decode>(
|
||||
db: &dyn Database<DbHash>,
|
||||
) -> sp_blockchain::Result<Option<Hash>> {
|
||||
match db.get(COLUMN_META, meta_keys::GENESIS_HASH) {
|
||||
Some(h) => match Decode::decode(&mut &h[..]) {
|
||||
Ok(h) => Ok(Some(h)),
|
||||
Err(err) => Err(sp_blockchain::Error::Backend(
|
||||
format!("Error decoding genesis hash: {}", err)
|
||||
)),
|
||||
Err(err) =>
|
||||
Err(sp_blockchain::Error::Backend(format!("Error decoding genesis hash: {}", err))),
|
||||
},
|
||||
None => Ok(None),
|
||||
}
|
||||
@@ -461,7 +457,7 @@ impl DatabaseType {
|
||||
|
||||
pub(crate) struct JoinInput<'a, 'b>(&'a [u8], &'b [u8]);
|
||||
|
||||
pub(crate) fn join_input<'a, 'b>(i1: &'a[u8], i2: &'b [u8]) -> JoinInput<'a, 'b> {
|
||||
pub(crate) fn join_input<'a, 'b>(i1: &'a [u8], i2: &'b [u8]) -> JoinInput<'a, 'b> {
|
||||
JoinInput(i1, i2)
|
||||
}
|
||||
|
||||
@@ -486,8 +482,8 @@ impl<'a, 'b> codec::Input for JoinInput<'a, 'b> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sp_runtime::testing::{Block as RawBlock, ExtrinsicWrapper};
|
||||
use codec::Input;
|
||||
use sp_runtime::testing::{Block as RawBlock, ExtrinsicWrapper};
|
||||
type Block = RawBlock<ExtrinsicWrapper<u32>>;
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user