diff --git a/substrate/client/db/src/lib.rs b/substrate/client/db/src/lib.rs index 6e138b2f70213..696fde3dba45b 100644 --- a/substrate/client/db/src/lib.rs +++ b/substrate/client/db/src/lib.rs @@ -300,12 +300,12 @@ impl Backend { } #[cfg(test)] - fn new_test() -> Self { + fn new_test(keep_blocks: u32) -> Self { use utils::NUM_COLUMNS; let db = Arc::new(::kvdb_memorydb::create(NUM_COLUMNS)); - Backend::from_kvdb(db as Arc<_>, PruningMode::keep_blocks(0), 0).expect("failed to create test-db") + Backend::from_kvdb(db as Arc<_>, PruningMode::keep_blocks(keep_blocks), 0).expect("failed to create test-db") } fn from_kvdb(db: Arc, pruning: PruningMode, finalization_window: u64) -> Result { @@ -454,10 +454,14 @@ impl client::backend::Backend for Backend< _ => {} } - self.blockchain.header(block).and_then(|maybe_hdr| maybe_hdr.map(|hdr| { - let root: H256 = H256::from_slice(hdr.state_root().as_ref()); - DbState::with_storage(self.storage.clone(), root) - }).ok_or_else(|| client::error::ErrorKind::UnknownBlock(format!("{:?}", block)).into())) + match self.blockchain.header(block) { + Ok(Some(ref hdr)) if !self.storage.state_db.is_pruned(hdr.number().as_()) => { + let root: H256 = H256::from_slice(hdr.state_root().as_ref()); + Ok(DbState::with_storage(self.storage.clone(), root)) + }, + Err(e) => Err(e), + _ => Err(client::error::ErrorKind::UnknownBlock(format!("{:?}", block)).into()), + } } } @@ -477,7 +481,7 @@ mod tests { #[test] fn block_hash_inserted_correctly() { - let db = Backend::::new_test(); + let db = Backend::::new_test(1); for i in 0..10 { assert!(db.blockchain().hash(i).unwrap().is_none()); @@ -516,7 +520,7 @@ mod tests { #[test] fn set_state_data() { - let db = Backend::::new_test(); + let db = Backend::::new_test(2); { let mut op = db.begin_operation(BlockId::Hash(Default::default())).unwrap(); let mut header = Header { @@ -595,7 +599,7 @@ mod tests { #[test] fn delete_only_when_negative_rc() { let key; - let backend = Backend::::new_test(); + let backend = Backend::::new_test(0); let hash = { let mut op = backend.begin_operation(BlockId::Hash(Default::default())).unwrap(); diff --git a/substrate/state-db/src/lib.rs b/substrate/state-db/src/lib.rs index 34fae02b57d9f..26ab67acbdfac 100644 --- a/substrate/state-db/src/lib.rs +++ b/substrate/state-db/src/lib.rs @@ -226,6 +226,10 @@ impl StateDbSync { return self.unfinalized.last_finalized_block_number() } + pub fn is_pruned(&self, number: u64) -> bool { + self.pruning.as_ref().map_or(false, |pruning| number < pruning.pending()) + } + fn prune(&mut self, commit: &mut CommitSet) { if let (&mut Some(ref mut pruning), &PruningMode::Constrained(ref constraints)) = (&mut self.pruning, &self.mode) { loop { @@ -241,7 +245,6 @@ impl StateDbSync { if pruning.next_hash().map_or(false, |h| pinned.contains(&h)) { break; } - pruning.prune_one(commit); } } @@ -328,6 +331,11 @@ impl StateDb { return self.db.read().best_finalized() } + /// Check if block is pruned away. + pub fn is_pruned(&self, number: u64) -> bool { + return self.db.read().is_pruned(number) + } + } #[cfg(test)] @@ -375,19 +383,24 @@ mod tests { #[test] fn prune_window_1() { - let (db, _) = make_test_db(PruningMode::Constrained(Constraints { + let (db, sdb) = make_test_db(PruningMode::Constrained(Constraints { max_blocks: Some(1), max_mem: None, })); + assert!(sdb.is_pruned(0)); + assert!(sdb.is_pruned(1)); + assert!(!sdb.is_pruned(2)); assert!(db.data_eq(&make_db(&[21, 3, 922, 93, 94]))); } #[test] fn prune_window_2() { - let (db, _) = make_test_db(PruningMode::Constrained(Constraints { + let (db, sdb) = make_test_db(PruningMode::Constrained(Constraints { max_blocks: Some(2), max_mem: None, })); + assert!(sdb.is_pruned(0)); + assert!(!sdb.is_pruned(1)); assert!(db.data_eq(&make_db(&[1, 21, 3, 921, 922, 93, 94]))); } } diff --git a/substrate/state-db/src/pruning.rs b/substrate/state-db/src/pruning.rs index 240e9bdcb32f8..aaacbd09b66ad 100644 --- a/substrate/state-db/src/pruning.rs +++ b/substrate/state-db/src/pruning.rs @@ -60,7 +60,7 @@ impl RefWindow { .map_err(|e| Error::Db(e))?; let pending_number: u64 = match last_pruned { Some(buffer) => u64::decode(&mut buffer.as_slice()).ok_or(Error::Decoding)? + 1, - None => 1, + None => 0, }; let mut block = pending_number; let mut pruning = RefWindow { @@ -69,7 +69,7 @@ impl RefWindow { pending_number: pending_number, }; // read the journal - trace!(target: "state-db", "Reading pruning journal. Last pruned #{}", pending_number - 1); + trace!(target: "state-db", "Reading pruning journal. Pending #{}", pending_number); loop { let journal_key = to_journal_key(block); match db.get_meta(&journal_key).map_err(|e| Error::Db(e))? { @@ -119,6 +119,10 @@ impl RefWindow { 0 } + pub fn pending(&self) -> u64 { + self.pending_number + } + /// Prune next block. Expects at least one block in the window. Adds changes to `commit`. pub fn prune_one(&mut self, commit: &mut CommitSet) { let pruned = self.death_rows.pop_front().expect("prune_one is only called with a non-empty window"); @@ -168,7 +172,7 @@ mod tests { fn created_from_empty_db() { let db = make_db(&[]); let pruning: RefWindow = RefWindow::new(&db).unwrap(); - assert_eq!(pruning.pending_number, 1); + assert_eq!(pruning.pending_number, 0); assert!(pruning.death_rows.is_empty()); assert!(pruning.death_index.is_empty()); } @@ -202,7 +206,7 @@ mod tests { assert!(db.data_eq(&make_db(&[2, 4, 5]))); assert!(pruning.death_rows.is_empty()); assert!(pruning.death_index.is_empty()); - assert_eq!(pruning.pending_number, 2); + assert_eq!(pruning.pending_number, 1); } #[test] @@ -227,7 +231,7 @@ mod tests { pruning.prune_one(&mut commit); db.commit(&commit); assert!(db.data_eq(&make_db(&[3, 4, 5]))); - assert_eq!(pruning.pending_number, 3); + assert_eq!(pruning.pending_number, 2); } #[test] @@ -258,6 +262,6 @@ mod tests { pruning.prune_one(&mut commit); db.commit(&commit); assert!(db.data_eq(&make_db(&[1, 3]))); - assert_eq!(pruning.pending_number, 4); + assert_eq!(pruning.pending_number, 3); } }