mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 17:01:09 +00:00
Support reference-counting state backend. (#5769)
* Optimize pinning * Ref counting state backend * Style Co-Authored-By: Wei Tang <hi@that.world> * Update Cargo.lock * Handle empty node Co-authored-by: Wei Tang <hi@that.world>
This commit is contained in:
Generated
+3
-2
@@ -4701,11 +4701,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "parity-db"
|
||||
version = "0.1.0"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4174d70be686b0d7cdee964b2d723e1461e28390c8804f01efc907d81043be9"
|
||||
checksum = "00d595e372d119261593297debbe4193811a4dc811d2a1ccbb8caaa6666ad7ab"
|
||||
dependencies = [
|
||||
"blake2-rfc",
|
||||
"crc32fast",
|
||||
"libc",
|
||||
"log",
|
||||
"memmap",
|
||||
|
||||
@@ -34,7 +34,7 @@ sp-trie = { version = "2.0.0-dev", path = "../../primitives/trie" }
|
||||
sp-consensus = { version = "0.8.0-dev", path = "../../primitives/consensus/common" }
|
||||
sp-blockchain = { version = "2.0.0-dev", path = "../../primitives/blockchain" }
|
||||
sp-database = { version = "2.0.0-dev", path = "../../primitives/database" }
|
||||
parity-db = { version = "0.1", optional = true }
|
||||
parity-db = { version = "0.1.2", optional = true }
|
||||
prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.8.0-dev", path = "../../utils/prometheus" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -109,8 +109,9 @@ pub type DbState<B> = sp_state_machine::TrieBackend<
|
||||
Arc<dyn sp_state_machine::Storage<HashFor<B>>>, HashFor<B>
|
||||
>;
|
||||
|
||||
const DB_HASH_LEN: usize = 32;
|
||||
/// Hash type that this backend uses for the database.
|
||||
pub type DbHash = [u8; 32];
|
||||
pub type DbHash = [u8; DB_HASH_LEN];
|
||||
|
||||
/// A reference tracking state.
|
||||
///
|
||||
@@ -314,6 +315,13 @@ impl DatabaseSettingsSrc {
|
||||
DatabaseSettingsSrc::Custom(_) => None,
|
||||
}
|
||||
}
|
||||
/// Check if database supports internal ref counting for state data.
|
||||
pub fn supports_ref_counting(&self) -> bool {
|
||||
match self {
|
||||
DatabaseSettingsSrc::ParityDb { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an instance of db-backed client.
|
||||
@@ -716,13 +724,18 @@ impl<Block: BlockT> sc_client_api::backend::BlockImportOperation<Block> for Bloc
|
||||
struct StorageDb<Block: BlockT> {
|
||||
pub db: Arc<dyn Database<DbHash>>,
|
||||
pub state_db: StateDb<Block::Hash, Vec<u8>>,
|
||||
prefix_keys: bool,
|
||||
}
|
||||
|
||||
impl<Block: BlockT> sp_state_machine::Storage<HashFor<Block>> for StorageDb<Block> {
|
||||
fn get(&self, key: &Block::Hash, prefix: Prefix) -> Result<Option<DBValue>, String> {
|
||||
let key = prefixed_key::<HashFor<Block>>(key, prefix);
|
||||
self.state_db.get(&key, self)
|
||||
.map_err(|e| format!("Database backend error: {:?}", e))
|
||||
if self.prefix_keys {
|
||||
let key = prefixed_key::<HashFor<Block>>(key, prefix);
|
||||
self.state_db.get(&key, self)
|
||||
} else {
|
||||
self.state_db.get(key.as_ref(), self)
|
||||
}
|
||||
.map_err(|e| format!("Database backend error: {:?}", e))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -843,11 +856,15 @@ impl<Block: BlockT> Backend<Block> {
|
||||
let map_e = |e: sc_state_db::Error<io::Error>| sp_blockchain::Error::from(
|
||||
format!("State database error: {:?}", e)
|
||||
);
|
||||
let state_db: StateDb<_, _> = StateDb::new(config.pruning.clone(), &StateMetaDb(&*db))
|
||||
.map_err(map_e)?;
|
||||
let state_db: StateDb<_, _> = StateDb::new(
|
||||
config.pruning.clone(),
|
||||
!config.source.supports_ref_counting(),
|
||||
&StateMetaDb(&*db),
|
||||
).map_err(map_e)?;
|
||||
let storage_db = StorageDb {
|
||||
db: db.clone(),
|
||||
state_db,
|
||||
prefix_keys: !config.source.supports_ref_counting(),
|
||||
};
|
||||
let offchain_storage = offchain::LocalStorage::new(db.clone());
|
||||
let changes_tries_storage = DbChangesTrieStorage::new(
|
||||
@@ -1112,17 +1129,32 @@ impl<Block: BlockT> Backend<Block> {
|
||||
let mut bytes: u64 = 0;
|
||||
let mut removal: u64 = 0;
|
||||
let mut bytes_removal: u64 = 0;
|
||||
for (key, (val, rc)) in operation.db_updates.drain() {
|
||||
for (mut key, (val, rc)) in operation.db_updates.drain() {
|
||||
if !self.storage.prefix_keys {
|
||||
// Strip prefix
|
||||
key.drain(0 .. key.len() - DB_HASH_LEN);
|
||||
};
|
||||
if rc > 0 {
|
||||
ops += 1;
|
||||
bytes += key.len() as u64 + val.len() as u64;
|
||||
|
||||
changeset.inserted.push((key, val.to_vec()));
|
||||
if rc == 1 {
|
||||
changeset.inserted.push((key, val.to_vec()));
|
||||
} else {
|
||||
changeset.inserted.push((key.clone(), val.to_vec()));
|
||||
for _ in 0 .. rc - 1 {
|
||||
changeset.inserted.push((key.clone(), Default::default()));
|
||||
}
|
||||
}
|
||||
} else if rc < 0 {
|
||||
removal += 1;
|
||||
bytes_removal += key.len() as u64;
|
||||
|
||||
changeset.deleted.push(key);
|
||||
if rc == -1 {
|
||||
changeset.deleted.push(key);
|
||||
} else {
|
||||
for _ in 0 .. -rc {
|
||||
changeset.deleted.push(key.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.state_usage.tally_writes_nodes(ops, bytes);
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
/// A `Database` adapter for parity-db.
|
||||
|
||||
use sp_database::{Database, Change, Transaction, ColumnId};
|
||||
use crate::utils::NUM_COLUMNS;
|
||||
use crate::columns;
|
||||
|
||||
struct DbAdapter(parity_db::Db);
|
||||
|
||||
@@ -30,8 +32,13 @@ fn handle_err<T>(result: parity_db::Result<T>) -> T {
|
||||
}
|
||||
|
||||
/// Wrap RocksDb database into a trait object that implements `sp_database::Database`
|
||||
pub fn open<H: Clone>(path: &std::path::Path, num_columns: u32) -> parity_db::Result<std::sync::Arc<dyn Database<H>>> {
|
||||
let db = parity_db::Db::with_columns(path, num_columns as u8)?;
|
||||
pub fn open<H: Clone>(path: &std::path::Path) -> parity_db::Result<std::sync::Arc<dyn Database<H>>> {
|
||||
let mut config = parity_db::Options::with_columns(path, NUM_COLUMNS as u8);
|
||||
let mut state_col = &mut config.columns[columns::STATE as usize];
|
||||
state_col.ref_counted = true;
|
||||
state_col.preimage = true;
|
||||
state_col.uniform = true;
|
||||
let db = parity_db::Db::open(&config)?;
|
||||
Ok(std::sync::Arc::new(DbAdapter(db)))
|
||||
}
|
||||
|
||||
|
||||
@@ -254,7 +254,7 @@ pub fn open_database<Block: BlockT>(
|
||||
},
|
||||
#[cfg(feature = "parity-db")]
|
||||
DatabaseSettingsSrc::ParityDb { path } => {
|
||||
crate::parity_db::open(&path, NUM_COLUMNS)
|
||||
crate::parity_db::open(&path)
|
||||
.map_err(|e| sp_blockchain::Error::Backend(format!("{:?}", e)))?
|
||||
},
|
||||
DatabaseSettingsSrc::Custom(db) => db.clone(),
|
||||
|
||||
@@ -201,9 +201,10 @@ struct StateDbSync<BlockHash: Hash, Key: Hash> {
|
||||
impl<BlockHash: Hash + MallocSizeOf, Key: Hash + MallocSizeOf> StateDbSync<BlockHash, Key> {
|
||||
fn new<D: MetaDb>(
|
||||
mode: PruningMode,
|
||||
ref_counting: bool,
|
||||
db: &D,
|
||||
) -> Result<StateDbSync<BlockHash, Key>, Error<D::Error>> {
|
||||
trace!(target: "state-db", "StateDb settings: {:?}", mode);
|
||||
trace!(target: "state-db", "StateDb settings: {:?}. Ref-counting: {}", mode, ref_counting);
|
||||
|
||||
// Check that settings match
|
||||
Self::check_meta(&mode, db)?;
|
||||
@@ -214,7 +215,7 @@ impl<BlockHash: Hash + MallocSizeOf, Key: Hash + MallocSizeOf> StateDbSync<Block
|
||||
max_mem: Some(_),
|
||||
..
|
||||
}) => unimplemented!(),
|
||||
PruningMode::Constrained(_) => Some(RefWindow::new(db)?),
|
||||
PruningMode::Constrained(_) => Some(RefWindow::new(db, ref_counting)?),
|
||||
PruningMode::ArchiveAll | PruningMode::ArchiveCanonical => None,
|
||||
};
|
||||
|
||||
@@ -387,8 +388,11 @@ impl<BlockHash: Hash + MallocSizeOf, Key: Hash + MallocSizeOf> StateDbSync<Block
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get<D: NodeDb>(&self, key: &Key, db: &D) -> Result<Option<DBValue>, Error<D::Error>>
|
||||
where Key: AsRef<D::Key>
|
||||
pub fn get<D: NodeDb, Q: ?Sized>(&self, key: &Q, db: &D) -> Result<Option<DBValue>, Error<D::Error>>
|
||||
where
|
||||
Q: AsRef<D::Key>,
|
||||
Key: std::borrow::Borrow<Q>,
|
||||
Q: std::hash::Hash + Eq,
|
||||
{
|
||||
if let Some(value) = self.non_canonical.get(key) {
|
||||
return Ok(Some(value));
|
||||
@@ -438,10 +442,11 @@ impl<BlockHash: Hash + MallocSizeOf, Key: Hash + MallocSizeOf> StateDb<BlockHash
|
||||
/// Creates a new instance. Does not expect any metadata in the database.
|
||||
pub fn new<D: MetaDb>(
|
||||
mode: PruningMode,
|
||||
ref_counting: bool,
|
||||
db: &D,
|
||||
) -> Result<StateDb<BlockHash, Key>, Error<D::Error>> {
|
||||
Ok(StateDb {
|
||||
db: RwLock::new(StateDbSync::new(mode, db)?)
|
||||
db: RwLock::new(StateDbSync::new(mode, ref_counting, db)?)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -475,8 +480,11 @@ impl<BlockHash: Hash + MallocSizeOf, Key: Hash + MallocSizeOf> StateDb<BlockHash
|
||||
}
|
||||
|
||||
/// Get a value from non-canonical/pruning overlay or the backing DB.
|
||||
pub fn get<D: NodeDb>(&self, key: &Key, db: &D) -> Result<Option<DBValue>, Error<D::Error>>
|
||||
where Key: AsRef<D::Key>
|
||||
pub fn get<D: NodeDb, Q: ?Sized>(&self, key: &Q, db: &D) -> Result<Option<DBValue>, Error<D::Error>>
|
||||
where
|
||||
Q: AsRef<D::Key>,
|
||||
Key: std::borrow::Borrow<Q>,
|
||||
Q: std::hash::Hash + Eq,
|
||||
{
|
||||
self.db.read().get(key, db)
|
||||
}
|
||||
@@ -523,7 +531,7 @@ mod tests {
|
||||
|
||||
fn make_test_db(settings: PruningMode) -> (TestDb, StateDb<H256, H256>) {
|
||||
let mut db = make_db(&[91, 921, 922, 93, 94]);
|
||||
let state_db = StateDb::new(settings, &db).unwrap();
|
||||
let state_db = StateDb::new(settings, false, &db).unwrap();
|
||||
|
||||
db.commit(
|
||||
&state_db
|
||||
@@ -638,7 +646,7 @@ mod tests {
|
||||
#[test]
|
||||
fn detects_incompatible_mode() {
|
||||
let mut db = make_db(&[]);
|
||||
let state_db = StateDb::new(PruningMode::ArchiveAll, &db).unwrap();
|
||||
let state_db = StateDb::new(PruningMode::ArchiveAll, false, &db).unwrap();
|
||||
db.commit(
|
||||
&state_db
|
||||
.insert_block::<io::Error>(
|
||||
@@ -650,7 +658,7 @@ mod tests {
|
||||
.unwrap(),
|
||||
);
|
||||
let new_mode = PruningMode::Constrained(Constraints { max_blocks: Some(2), max_mem: None });
|
||||
let state_db: Result<StateDb<H256, H256>, _> = StateDb::new(new_mode, &db);
|
||||
let state_db: Result<StateDb<H256, H256>, _> = StateDb::new(new_mode, false, &db);
|
||||
assert!(state_db.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ pub struct NonCanonicalOverlay<BlockHash: Hash, Key: Hash> {
|
||||
values: HashMap<Key, (u32, DBValue)>, //ref counted
|
||||
//would be deleted but kept around because block is pinned, ref counted.
|
||||
pinned: HashMap<BlockHash, u32>,
|
||||
pinned_insertions: HashMap<BlockHash, Vec<Key>>,
|
||||
pinned_insertions: HashMap<BlockHash, (Vec<Key>, u32)>,
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode)]
|
||||
@@ -90,25 +90,44 @@ fn discard_values<Key: Hash>(values: &mut HashMap<Key, (u32, DBValue)>, inserted
|
||||
}
|
||||
|
||||
fn discard_descendants<BlockHash: Hash, Key: Hash>(
|
||||
levels: &mut VecDeque<Vec<BlockOverlay<BlockHash, Key>>>,
|
||||
levels: &mut (&mut [Vec<BlockOverlay<BlockHash, Key>>], &mut [Vec<BlockOverlay<BlockHash, Key>>]),
|
||||
mut values: &mut HashMap<Key, (u32, DBValue)>,
|
||||
index: usize,
|
||||
parents: &mut HashMap<BlockHash, BlockHash>,
|
||||
pinned: &HashMap<BlockHash, u32>,
|
||||
pinned_insertions: &mut HashMap<BlockHash, Vec<Key>>,
|
||||
pinned_insertions: &mut HashMap<BlockHash, (Vec<Key>, u32)>,
|
||||
hash: &BlockHash,
|
||||
) {
|
||||
let mut discarded = Vec::new();
|
||||
if let Some(level) = levels.get_mut(index) {
|
||||
) -> u32 {
|
||||
let (first, mut remainder) = if let Some((first, rest)) = levels.0.split_first_mut() {
|
||||
(Some(first), (rest, &mut levels.1[..]))
|
||||
} else {
|
||||
if let Some((first, rest)) = levels.1.split_first_mut() {
|
||||
(Some(first), (&mut levels.0[..], rest))
|
||||
} else {
|
||||
(None, (&mut levels.0[..], &mut levels.1[..]))
|
||||
}
|
||||
};
|
||||
let mut pinned_children = 0;
|
||||
if let Some(level) = first {
|
||||
*level = level.drain(..).filter_map(|overlay| {
|
||||
let parent = parents.get(&overlay.hash)
|
||||
.expect("there is a parent entry for each entry in levels; qed");
|
||||
|
||||
if parent == hash {
|
||||
discarded.push(overlay.hash.clone());
|
||||
let mut num_pinned = discard_descendants(
|
||||
&mut remainder,
|
||||
values,
|
||||
parents,
|
||||
pinned,
|
||||
pinned_insertions,
|
||||
&overlay.hash
|
||||
);
|
||||
if pinned.contains_key(&overlay.hash) {
|
||||
num_pinned += 1;
|
||||
}
|
||||
if num_pinned != 0 {
|
||||
// save to be discarded later.
|
||||
pinned_insertions.insert(overlay.hash.clone(), overlay.inserted);
|
||||
pinned_insertions.insert(overlay.hash.clone(), (overlay.inserted, num_pinned));
|
||||
pinned_children += num_pinned;
|
||||
} else {
|
||||
// discard immediately.
|
||||
parents.remove(&overlay.hash);
|
||||
@@ -120,9 +139,7 @@ fn discard_descendants<BlockHash: Hash, Key: Hash>(
|
||||
}
|
||||
}).collect();
|
||||
}
|
||||
for hash in discarded {
|
||||
discard_descendants(levels, values, index + 1, parents, pinned, pinned_insertions, &hash);
|
||||
}
|
||||
pinned_children
|
||||
}
|
||||
|
||||
impl<BlockHash: Hash, Key: Hash> NonCanonicalOverlay<BlockHash, Key> {
|
||||
@@ -346,19 +363,23 @@ impl<BlockHash: Hash, Key: Hash> NonCanonicalOverlay<BlockHash, Key> {
|
||||
|
||||
// discard unfinalized overlays and values
|
||||
for (i, overlay) in level.into_iter().enumerate() {
|
||||
if i != index {
|
||||
let mut pinned_children = if i != index {
|
||||
discard_descendants(
|
||||
&mut self.levels,
|
||||
&mut self.levels.as_mut_slices(),
|
||||
&mut self.values,
|
||||
0,
|
||||
&mut self.parents,
|
||||
&self.pinned,
|
||||
&mut self.pinned_insertions,
|
||||
&overlay.hash,
|
||||
);
|
||||
}
|
||||
)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if self.pinned.contains_key(&overlay.hash) {
|
||||
self.pinned_insertions.insert(overlay.hash.clone(), overlay.inserted);
|
||||
pinned_children += 1;
|
||||
}
|
||||
if pinned_children != 0 {
|
||||
self.pinned_insertions.insert(overlay.hash.clone(), (overlay.inserted, pinned_children));
|
||||
} else {
|
||||
self.parents.remove(&overlay.hash);
|
||||
discard_values(&mut self.values, overlay.inserted);
|
||||
@@ -372,7 +393,11 @@ impl<BlockHash: Hash, Key: Hash> NonCanonicalOverlay<BlockHash, Key> {
|
||||
}
|
||||
|
||||
/// Get a value from the node overlay. This searches in every existing changeset.
|
||||
pub fn get(&self, key: &Key) -> Option<DBValue> {
|
||||
pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<DBValue>
|
||||
where
|
||||
Key: std::borrow::Borrow<Q>,
|
||||
Q: std::hash::Hash + Eq,
|
||||
{
|
||||
if let Some((_, value)) = self.values.get(&key) {
|
||||
return Some(value.clone());
|
||||
}
|
||||
@@ -435,37 +460,47 @@ impl<BlockHash: Hash, Key: Hash> NonCanonicalOverlay<BlockHash, Key> {
|
||||
debug_assert!(false, "Trying to pin pending state");
|
||||
return;
|
||||
}
|
||||
// Also pin all parents
|
||||
let mut parent = Some(hash);
|
||||
while let Some(hash) = parent {
|
||||
let refs = self.pinned.entry(hash.clone()).or_default();
|
||||
if *refs == 0 {
|
||||
trace!(target: "state-db-pin", "Pinned non-canon block: {:?}", hash);
|
||||
}
|
||||
*refs += 1;
|
||||
parent = self.parents.get(hash);
|
||||
let refs = self.pinned.entry(hash.clone()).or_default();
|
||||
if *refs == 0 {
|
||||
trace!(target: "state-db-pin", "Pinned non-canon block: {:?}", hash);
|
||||
}
|
||||
*refs += 1;
|
||||
}
|
||||
|
||||
/// Discard pinned state
|
||||
pub fn unpin(&mut self, hash: &BlockHash) {
|
||||
// Also unpin all parents
|
||||
let mut parent = Some(hash.clone());
|
||||
while let Some(hash) = parent {
|
||||
parent = self.parents.get(&hash).cloned();
|
||||
match self.pinned.entry(hash.clone()) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
*entry.get_mut() -= 1;
|
||||
if *entry.get() == 0 {
|
||||
entry.remove();
|
||||
if let Some(inserted) = self.pinned_insertions.remove(&hash) {
|
||||
let removed = match self.pinned.entry(hash.clone()) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
*entry.get_mut() -= 1;
|
||||
if *entry.get() == 0 {
|
||||
entry.remove();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
Entry::Vacant(_) => false,
|
||||
};
|
||||
|
||||
if removed {
|
||||
let mut parent = Some(hash.clone());
|
||||
while let Some(hash) = parent {
|
||||
parent = self.parents.get(&hash).cloned();
|
||||
match self.pinned_insertions.entry(hash.clone()) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
entry.get_mut().1 -= 1;
|
||||
if entry.get().1 == 0 {
|
||||
let (inserted, _) = entry.remove();
|
||||
trace!(target: "state-db-pin", "Discarding unpinned non-canon block: {:?}", hash);
|
||||
discard_values(&mut self.values, inserted);
|
||||
self.parents.remove(&hash);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
Entry::Vacant(_) => {},
|
||||
},
|
||||
Entry::Vacant(_) => break,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ pub struct RefWindow<BlockHash: Hash, Key: Hash> {
|
||||
/// Number of calls of `prune_one` after
|
||||
/// last call `apply_pending` or `revert_pending`
|
||||
pending_prunings: usize,
|
||||
/// Keep track of re-inserted keys and do not delete them when pruning.
|
||||
/// Setting this to false requires backend that supports reference
|
||||
/// counting.
|
||||
count_insertions: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, parity_util_mem_derive::MallocSizeOf)]
|
||||
@@ -66,7 +70,7 @@ fn to_journal_key(block: u64) -> Vec<u8> {
|
||||
}
|
||||
|
||||
impl<BlockHash: Hash, Key: Hash> RefWindow<BlockHash, Key> {
|
||||
pub fn new<D: MetaDb>(db: &D) -> Result<RefWindow<BlockHash, Key>, Error<D::Error>> {
|
||||
pub fn new<D: MetaDb>(db: &D, count_insertions: bool) -> Result<RefWindow<BlockHash, Key>, Error<D::Error>> {
|
||||
let last_pruned = db.get_meta(&to_meta_key(LAST_PRUNED, &()))
|
||||
.map_err(|e| Error::Db(e))?;
|
||||
let pending_number: u64 = match last_pruned {
|
||||
@@ -80,6 +84,7 @@ impl<BlockHash: Hash, Key: Hash> RefWindow<BlockHash, Key> {
|
||||
pending_number: pending_number,
|
||||
pending_canonicalizations: 0,
|
||||
pending_prunings: 0,
|
||||
count_insertions,
|
||||
};
|
||||
// read the journal
|
||||
trace!(target: "state-db", "Reading pruning journal. Pending #{}", pending_number);
|
||||
@@ -99,17 +104,19 @@ impl<BlockHash: Hash, Key: Hash> RefWindow<BlockHash, Key> {
|
||||
}
|
||||
|
||||
fn import<I: IntoIterator<Item=Key>>(&mut self, hash: &BlockHash, journal_key: Vec<u8>, inserted: I, deleted: Vec<Key>) {
|
||||
// remove all re-inserted keys from death rows
|
||||
for k in inserted {
|
||||
if let Some(block) = self.death_index.remove(&k) {
|
||||
self.death_rows[(block - self.pending_number) as usize].deleted.remove(&k);
|
||||
if self.count_insertions {
|
||||
// remove all re-inserted keys from death rows
|
||||
for k in inserted {
|
||||
if let Some(block) = self.death_index.remove(&k) {
|
||||
self.death_rows[(block - self.pending_number) as usize].deleted.remove(&k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add new keys
|
||||
let imported_block = self.pending_number + self.death_rows.len() as u64;
|
||||
for k in deleted.iter() {
|
||||
self.death_index.insert(k.clone(), imported_block);
|
||||
// add new keys
|
||||
let imported_block = self.pending_number + self.death_rows.len() as u64;
|
||||
for k in deleted.iter() {
|
||||
self.death_index.insert(k.clone(), imported_block);
|
||||
}
|
||||
}
|
||||
self.death_rows.push_back(
|
||||
DeathRow {
|
||||
@@ -157,7 +164,11 @@ impl<BlockHash: Hash, Key: Hash> RefWindow<BlockHash, Key> {
|
||||
/// Add a change set to the window. Creates a journal record and pushes it to `commit`
|
||||
pub fn note_canonical(&mut self, hash: &BlockHash, commit: &mut CommitSet<Key>) {
|
||||
trace!(target: "state-db", "Adding to pruning window: {:?} ({} inserted, {} deleted)", hash, commit.data.inserted.len(), commit.data.deleted.len());
|
||||
let inserted = commit.data.inserted.iter().map(|(k, _)| k.clone()).collect();
|
||||
let inserted = if self.count_insertions {
|
||||
commit.data.inserted.iter().map(|(k, _)| k.clone()).collect()
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let deleted = ::std::mem::replace(&mut commit.data.deleted, Vec::new());
|
||||
let journal_record = JournalRecord {
|
||||
hash: hash.clone(),
|
||||
@@ -177,8 +188,10 @@ impl<BlockHash: Hash, Key: Hash> RefWindow<BlockHash, Key> {
|
||||
for _ in 0 .. self.pending_prunings {
|
||||
let pruned = self.death_rows.pop_front().expect("pending_prunings is always < death_rows.len()");
|
||||
trace!(target: "state-db", "Applying pruning {:?} ({} deleted)", pruned.hash, pruned.deleted.len());
|
||||
for k in pruned.deleted.iter() {
|
||||
self.death_index.remove(&k);
|
||||
if self.count_insertions {
|
||||
for k in pruned.deleted.iter() {
|
||||
self.death_index.remove(&k);
|
||||
}
|
||||
}
|
||||
self.pending_number += 1;
|
||||
}
|
||||
@@ -192,8 +205,10 @@ impl<BlockHash: Hash, Key: Hash> RefWindow<BlockHash, Key> {
|
||||
// We don't bother to track and revert that for now. This means that a few nodes might end up no being
|
||||
// deleted in case transaction fails and `revert_pending` is called.
|
||||
self.death_rows.truncate(self.death_rows.len() - self.pending_canonicalizations);
|
||||
let new_max_block = self.death_rows.len() as u64 + self.pending_number;
|
||||
self.death_index.retain(|_, block| *block < new_max_block);
|
||||
if self.count_insertions {
|
||||
let new_max_block = self.death_rows.len() as u64 + self.pending_number;
|
||||
self.death_index.retain(|_, block| *block < new_max_block);
|
||||
}
|
||||
self.pending_canonicalizations = 0;
|
||||
self.pending_prunings = 0;
|
||||
}
|
||||
@@ -207,7 +222,7 @@ mod tests {
|
||||
use crate::test::{make_db, make_commit, TestDb};
|
||||
|
||||
fn check_journal(pruning: &RefWindow<H256, H256>, db: &TestDb) {
|
||||
let restored: RefWindow<H256, H256> = RefWindow::new(db).unwrap();
|
||||
let restored: RefWindow<H256, H256> = RefWindow::new(db, pruning.count_insertions).unwrap();
|
||||
assert_eq!(pruning.pending_number, restored.pending_number);
|
||||
assert_eq!(pruning.death_rows, restored.death_rows);
|
||||
assert_eq!(pruning.death_index, restored.death_index);
|
||||
@@ -216,7 +231,7 @@ mod tests {
|
||||
#[test]
|
||||
fn created_from_empty_db() {
|
||||
let db = make_db(&[]);
|
||||
let pruning: RefWindow<H256, H256> = RefWindow::new(&db).unwrap();
|
||||
let pruning: RefWindow<H256, H256> = RefWindow::new(&db, true).unwrap();
|
||||
assert_eq!(pruning.pending_number, 0);
|
||||
assert!(pruning.death_rows.is_empty());
|
||||
assert!(pruning.death_index.is_empty());
|
||||
@@ -225,7 +240,7 @@ mod tests {
|
||||
#[test]
|
||||
fn prune_empty() {
|
||||
let db = make_db(&[]);
|
||||
let mut pruning: RefWindow<H256, H256> = RefWindow::new(&db).unwrap();
|
||||
let mut pruning: RefWindow<H256, H256> = RefWindow::new(&db, true).unwrap();
|
||||
let mut commit = CommitSet::default();
|
||||
pruning.prune_one(&mut commit);
|
||||
assert_eq!(pruning.pending_number, 0);
|
||||
@@ -238,7 +253,7 @@ mod tests {
|
||||
#[test]
|
||||
fn prune_one() {
|
||||
let mut db = make_db(&[1, 2, 3]);
|
||||
let mut pruning: RefWindow<H256, H256> = RefWindow::new(&db).unwrap();
|
||||
let mut pruning: RefWindow<H256, H256> = RefWindow::new(&db, true).unwrap();
|
||||
let mut commit = make_commit(&[4, 5], &[1, 3]);
|
||||
let h = H256::random();
|
||||
pruning.note_canonical(&h, &mut commit);
|
||||
@@ -267,7 +282,7 @@ mod tests {
|
||||
#[test]
|
||||
fn prune_two() {
|
||||
let mut db = make_db(&[1, 2, 3]);
|
||||
let mut pruning: RefWindow<H256, H256> = RefWindow::new(&db).unwrap();
|
||||
let mut pruning: RefWindow<H256, H256> = RefWindow::new(&db, true).unwrap();
|
||||
let mut commit = make_commit(&[4], &[1]);
|
||||
pruning.note_canonical(&H256::random(), &mut commit);
|
||||
db.commit(&commit);
|
||||
@@ -295,7 +310,7 @@ mod tests {
|
||||
#[test]
|
||||
fn prune_two_pending() {
|
||||
let mut db = make_db(&[1, 2, 3]);
|
||||
let mut pruning: RefWindow<H256, H256> = RefWindow::new(&db).unwrap();
|
||||
let mut pruning: RefWindow<H256, H256> = RefWindow::new(&db, true).unwrap();
|
||||
let mut commit = make_commit(&[4], &[1]);
|
||||
pruning.note_canonical(&H256::random(), &mut commit);
|
||||
db.commit(&commit);
|
||||
@@ -318,7 +333,7 @@ mod tests {
|
||||
#[test]
|
||||
fn reinserted_survives() {
|
||||
let mut db = make_db(&[1, 2, 3]);
|
||||
let mut pruning: RefWindow<H256, H256> = RefWindow::new(&db).unwrap();
|
||||
let mut pruning: RefWindow<H256, H256> = RefWindow::new(&db, true).unwrap();
|
||||
let mut commit = make_commit(&[], &[2]);
|
||||
pruning.note_canonical(&H256::random(), &mut commit);
|
||||
db.commit(&commit);
|
||||
@@ -351,7 +366,7 @@ mod tests {
|
||||
#[test]
|
||||
fn reinserted_survive_pending() {
|
||||
let mut db = make_db(&[1, 2, 3]);
|
||||
let mut pruning: RefWindow<H256, H256> = RefWindow::new(&db).unwrap();
|
||||
let mut pruning: RefWindow<H256, H256> = RefWindow::new(&db, true).unwrap();
|
||||
let mut commit = make_commit(&[], &[2]);
|
||||
pruning.note_canonical(&H256::random(), &mut commit);
|
||||
db.commit(&commit);
|
||||
@@ -377,4 +392,30 @@ mod tests {
|
||||
pruning.apply_pending();
|
||||
assert_eq!(pruning.pending_number, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reinserted_ignores() {
|
||||
let mut db = make_db(&[1, 2, 3]);
|
||||
let mut pruning: RefWindow<H256, H256> = RefWindow::new(&db, false).unwrap();
|
||||
let mut commit = make_commit(&[], &[2]);
|
||||
pruning.note_canonical(&H256::random(), &mut commit);
|
||||
db.commit(&commit);
|
||||
let mut commit = make_commit(&[2], &[]);
|
||||
pruning.note_canonical(&H256::random(), &mut commit);
|
||||
db.commit(&commit);
|
||||
let mut commit = make_commit(&[], &[2]);
|
||||
pruning.note_canonical(&H256::random(), &mut commit);
|
||||
db.commit(&commit);
|
||||
assert!(db.data_eq(&make_db(&[1, 2, 3])));
|
||||
pruning.apply_pending();
|
||||
|
||||
check_journal(&pruning, &db);
|
||||
|
||||
let mut commit = CommitSet::default();
|
||||
pruning.prune_one(&mut commit);
|
||||
db.commit(&commit);
|
||||
assert!(db.data_eq(&make_db(&[1, 3])));
|
||||
assert!(pruning.death_index.is_empty());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -129,11 +129,8 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
|
||||
}
|
||||
|
||||
fn pairs(&self) -> Vec<(StorageKey, StorageValue)> {
|
||||
let mut read_overlay = S::Overlay::default();
|
||||
let eph = Ephemeral::new(self.essence.backend_storage(), &mut read_overlay);
|
||||
|
||||
let collect_all = || -> Result<_, Box<TrieError<H::Out>>> {
|
||||
let trie = TrieDB::<H>::new(&eph, self.essence.root())?;
|
||||
let trie = TrieDB::<H>::new(self.essence(), self.essence.root())?;
|
||||
let mut v = Vec::new();
|
||||
for x in trie.iter()? {
|
||||
let (key, value) = x?;
|
||||
@@ -153,11 +150,8 @@ impl<S: TrieBackendStorage<H>, H: Hasher> Backend<H> for TrieBackend<S, H> where
|
||||
}
|
||||
|
||||
fn keys(&self, prefix: &[u8]) -> Vec<StorageKey> {
|
||||
let mut read_overlay = S::Overlay::default();
|
||||
let eph = Ephemeral::new(self.essence.backend_storage(), &mut read_overlay);
|
||||
|
||||
let collect_all = || -> Result<_, Box<TrieError<H::Out>>> {
|
||||
let trie = TrieDB::<H>::new(&eph, self.essence.root())?;
|
||||
let trie = TrieDB::<H>::new(self.essence(), self.essence.root())?;
|
||||
let mut v = Vec::new();
|
||||
for x in trie.iter()? {
|
||||
let (key, _) = x?;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use log::{debug, warn};
|
||||
use hash_db::{self, Hasher, EMPTY_PREFIX, Prefix};
|
||||
use hash_db::{self, Hasher, Prefix};
|
||||
use sp_trie::{Trie, MemoryDB, PrefixedMemoryDB, DBValue,
|
||||
empty_child_trie_root, read_trie_value, read_child_trie_value,
|
||||
for_keys_in_child_trie, KeySpacedDB, TrieDBIterator};
|
||||
@@ -39,6 +39,7 @@ pub trait Storage<H: Hasher>: Send + Sync {
|
||||
pub struct TrieBackendEssence<S: TrieBackendStorage<H>, H: Hasher> {
|
||||
storage: S,
|
||||
root: H::Out,
|
||||
empty: H::Out,
|
||||
}
|
||||
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out: Encode {
|
||||
@@ -47,6 +48,7 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
TrieBackendEssence {
|
||||
storage,
|
||||
root,
|
||||
empty: H::hash(&[0u8]),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,18 +118,13 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
child_info: Option<&ChildInfo>,
|
||||
key: &[u8],
|
||||
) -> Result<Option<StorageKey>, String> {
|
||||
let mut read_overlay = S::Overlay::default();
|
||||
let eph = Ephemeral {
|
||||
storage: &self.storage,
|
||||
overlay: &mut read_overlay,
|
||||
};
|
||||
let dyn_eph: &dyn hash_db::HashDBRef<_, _>;
|
||||
let keyspace_eph;
|
||||
if let Some(child_info) = child_info.as_ref() {
|
||||
keyspace_eph = KeySpacedDB::new(&eph, child_info.keyspace());
|
||||
keyspace_eph = KeySpacedDB::new(self, child_info.keyspace());
|
||||
dyn_eph = &keyspace_eph;
|
||||
} else {
|
||||
dyn_eph = &eph;
|
||||
dyn_eph = self;
|
||||
}
|
||||
|
||||
let trie = TrieDB::<H>::new(dyn_eph, root)
|
||||
@@ -161,15 +158,9 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
|
||||
/// Get the value of storage at given key.
|
||||
pub fn storage(&self, key: &[u8]) -> Result<Option<StorageValue>, String> {
|
||||
let mut read_overlay = S::Overlay::default();
|
||||
let eph = Ephemeral {
|
||||
storage: &self.storage,
|
||||
overlay: &mut read_overlay,
|
||||
};
|
||||
|
||||
let map_e = |e| format!("Trie lookup error: {}", e);
|
||||
|
||||
read_trie_value::<Layout<H>, _>(&eph, &self.root, key).map_err(map_e)
|
||||
read_trie_value::<Layout<H>, _>(self, &self.root, key).map_err(map_e)
|
||||
}
|
||||
|
||||
/// Get the value of child storage at given key.
|
||||
@@ -181,15 +172,9 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
let root = self.child_root(child_info)?
|
||||
.unwrap_or(empty_child_trie_root::<Layout<H>>().encode());
|
||||
|
||||
let mut read_overlay = S::Overlay::default();
|
||||
let eph = Ephemeral {
|
||||
storage: &self.storage,
|
||||
overlay: &mut read_overlay,
|
||||
};
|
||||
|
||||
let map_e = |e| format!("Trie lookup error: {}", e);
|
||||
|
||||
read_child_trie_value::<Layout<H>, _>(child_info.keyspace(), &eph, &root, key)
|
||||
read_child_trie_value::<Layout<H>, _>(child_info.keyspace(), self, &root, key)
|
||||
.map_err(map_e)
|
||||
}
|
||||
|
||||
@@ -207,15 +192,9 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
}
|
||||
};
|
||||
|
||||
let mut read_overlay = S::Overlay::default();
|
||||
let eph = Ephemeral {
|
||||
storage: &self.storage,
|
||||
overlay: &mut read_overlay,
|
||||
};
|
||||
|
||||
if let Err(e) = for_keys_in_child_trie::<Layout<H>, _, Ephemeral<S, H>>(
|
||||
if let Err(e) = for_keys_in_child_trie::<Layout<H>, _, _>(
|
||||
child_info.keyspace(),
|
||||
&eph,
|
||||
self,
|
||||
&root,
|
||||
f,
|
||||
) {
|
||||
@@ -254,12 +233,6 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
mut f: F,
|
||||
child_info: Option<&ChildInfo>,
|
||||
) {
|
||||
let mut read_overlay = S::Overlay::default();
|
||||
let eph = Ephemeral {
|
||||
storage: &self.storage,
|
||||
overlay: &mut read_overlay,
|
||||
};
|
||||
|
||||
let mut iter = move |db| -> Result<(), Box<TrieError<H::Out>>> {
|
||||
let trie = TrieDB::<H>::new(db, root)?;
|
||||
|
||||
@@ -275,10 +248,10 @@ impl<S: TrieBackendStorage<H>, H: Hasher> TrieBackendEssence<S, H> where H::Out:
|
||||
};
|
||||
|
||||
let result = if let Some(child_info) = child_info {
|
||||
let db = KeySpacedDB::new(&eph, child_info.keyspace());
|
||||
let db = KeySpacedDB::new(self, child_info.keyspace());
|
||||
iter(&db)
|
||||
} else {
|
||||
iter(&eph)
|
||||
iter(self)
|
||||
};
|
||||
if let Err(e) = result {
|
||||
debug!(target: "trie", "Error while iterating by prefix: {}", e);
|
||||
@@ -296,15 +269,6 @@ pub(crate) struct Ephemeral<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> {
|
||||
overlay: &'a mut S::Overlay,
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> hash_db::AsPlainDB<H::Out, DBValue>
|
||||
for Ephemeral<'a, S, H>
|
||||
{
|
||||
fn as_plain_db<'b>(&'b self) -> &'b (dyn hash_db::PlainDB<H::Out, DBValue> + 'b) { self }
|
||||
fn as_plain_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::PlainDB<H::Out, DBValue> + 'b) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + TrieBackendStorage<H>, H: 'a + Hasher> hash_db::AsHashDB<H, DBValue>
|
||||
for Ephemeral<'a, S, H>
|
||||
{
|
||||
@@ -321,43 +285,6 @@ impl<'a, S: TrieBackendStorage<H>, H: Hasher> Ephemeral<'a, S, H> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + TrieBackendStorage<H>, H: Hasher> hash_db::PlainDB<H::Out, DBValue>
|
||||
for Ephemeral<'a, S, H>
|
||||
{
|
||||
fn get(&self, key: &H::Out) -> Option<DBValue> {
|
||||
if let Some(val) = hash_db::HashDB::get(self.overlay, key, EMPTY_PREFIX) {
|
||||
Some(val)
|
||||
} else {
|
||||
match self.storage.get(&key, EMPTY_PREFIX) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
warn!(target: "trie", "Failed to read from DB: {}", e);
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, key: &H::Out) -> bool {
|
||||
hash_db::HashDB::get(self, key, EMPTY_PREFIX).is_some()
|
||||
}
|
||||
|
||||
fn emplace(&mut self, key: H::Out, value: DBValue) {
|
||||
hash_db::HashDB::emplace(self.overlay, key, EMPTY_PREFIX, value)
|
||||
}
|
||||
|
||||
fn remove(&mut self, key: &H::Out) {
|
||||
hash_db::HashDB::remove(self.overlay, key, EMPTY_PREFIX)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + TrieBackendStorage<H>, H: Hasher> hash_db::PlainDBRef<H::Out, DBValue>
|
||||
for Ephemeral<'a, S, H>
|
||||
{
|
||||
fn get(&self, key: &H::Out) -> Option<DBValue> { hash_db::PlainDB::get(self, key) }
|
||||
fn contains(&self, key: &H::Out) -> bool { hash_db::PlainDB::contains(self, key) }
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + TrieBackendStorage<H>, H: Hasher> hash_db::HashDB<H, DBValue>
|
||||
for Ephemeral<'a, S, H>
|
||||
{
|
||||
@@ -438,6 +365,59 @@ impl<H: Hasher> TrieBackendStorage<H> for MemoryDB<H> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> hash_db::AsHashDB<H, DBValue>
|
||||
for TrieBackendEssence<S, H>
|
||||
{
|
||||
fn as_hash_db<'b>(&'b self) -> &'b (dyn hash_db::HashDB<H, DBValue> + 'b) { self }
|
||||
fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn hash_db::HashDB<H, DBValue> + 'b) { self }
|
||||
}
|
||||
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> hash_db::HashDB<H, DBValue>
|
||||
for TrieBackendEssence<S, H>
|
||||
{
|
||||
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<DBValue> {
|
||||
if *key == self.empty {
|
||||
return Some([0u8].to_vec())
|
||||
}
|
||||
match self.storage.get(&key, prefix) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
warn!(target: "trie", "Failed to read from DB: {}", e);
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(&self, key: &H::Out, prefix: Prefix) -> bool {
|
||||
hash_db::HashDB::get(self, key, prefix).is_some()
|
||||
}
|
||||
|
||||
fn insert(&mut self, _prefix: Prefix, _value: &[u8]) -> H::Out {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn emplace(&mut self, _key: H::Out, _prefix: Prefix, _value: DBValue) {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn remove(&mut self, _key: &H::Out, _prefix: Prefix) {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: TrieBackendStorage<H>, H: Hasher> hash_db::HashDBRef<H, DBValue>
|
||||
for TrieBackendEssence<S, H>
|
||||
{
|
||||
fn get(&self, key: &H::Out, prefix: Prefix) -> Option<DBValue> {
|
||||
hash_db::HashDB::get(self, key, prefix)
|
||||
}
|
||||
|
||||
fn contains(&self, key: &H::Out, prefix: Prefix) -> bool {
|
||||
hash_db::HashDB::contains(self, key, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use sp_core::{Blake2Hasher, H256};
|
||||
|
||||
@@ -86,8 +86,6 @@ pub trait AsHashDB<H: Hasher>: hash_db::AsHashDB<H, trie_db::DBValue> {}
|
||||
impl<H: Hasher, T: hash_db::AsHashDB<H, trie_db::DBValue>> AsHashDB<H> for T {}
|
||||
/// Reexport from `hash_db`, with genericity set for `Hasher` trait.
|
||||
pub type HashDB<'a, H> = dyn hash_db::HashDB<H, trie_db::DBValue> + 'a;
|
||||
/// Reexport from `hash_db`, with genericity set for key only.
|
||||
pub type PlainDB<'a, K> = dyn hash_db::PlainDB<K, trie_db::DBValue> + 'a;
|
||||
/// Reexport from `hash_db`, with genericity set for `Hasher` trait.
|
||||
/// This uses a `KeyFunction` for prefixing keys internally (avoiding
|
||||
/// key conflict for non random keys).
|
||||
@@ -244,7 +242,6 @@ pub fn child_delta_trie_root<L: TrieConfiguration, I, A, B, DB, RD>(
|
||||
B: AsRef<[u8]>,
|
||||
RD: AsRef<[u8]>,
|
||||
DB: hash_db::HashDB<L::Hash, trie_db::DBValue>
|
||||
+ hash_db::PlainDB<TrieHash<L>, trie_db::DBValue>,
|
||||
{
|
||||
let mut root = TrieHash::<L>::default();
|
||||
// root is fetched from DB, not writable by runtime, so it's always valid.
|
||||
@@ -274,7 +271,6 @@ pub fn for_keys_in_child_trie<L: TrieConfiguration, F: FnMut(&[u8]), DB>(
|
||||
) -> Result<(), Box<TrieError<L>>>
|
||||
where
|
||||
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>
|
||||
+ hash_db::PlainDBRef<TrieHash<L>, trie_db::DBValue>,
|
||||
{
|
||||
let mut root = TrieHash::<L>::default();
|
||||
// root is fetched from DB, not writable by runtime, so it's always valid.
|
||||
@@ -324,7 +320,6 @@ pub fn read_child_trie_value<L: TrieConfiguration, DB>(
|
||||
) -> Result<Option<Vec<u8>>, Box<TrieError<L>>>
|
||||
where
|
||||
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>
|
||||
+ hash_db::PlainDBRef<TrieHash<L>, trie_db::DBValue>,
|
||||
{
|
||||
let mut root = TrieHash::<L>::default();
|
||||
// root is fetched from DB, not writable by runtime, so it's always valid.
|
||||
@@ -344,7 +339,6 @@ pub fn read_child_trie_value_with<L: TrieConfiguration, Q: Query<L::Hash, Item=D
|
||||
) -> Result<Option<Vec<u8>>, Box<TrieError<L>>>
|
||||
where
|
||||
DB: hash_db::HashDBRef<L::Hash, trie_db::DBValue>
|
||||
+ hash_db::PlainDBRef<TrieHash<L>, trie_db::DBValue>,
|
||||
{
|
||||
let mut root = TrieHash::<L>::default();
|
||||
// root is fetched from DB, not writable by runtime, so it's always valid.
|
||||
|
||||
Reference in New Issue
Block a user