1mod noncanonical;
33mod pruning;
34#[cfg(test)]
35mod test;
36
37use codec::Codec;
38use log::trace;
39use noncanonical::NonCanonicalOverlay;
40use parking_lot::RwLock;
41use pruning::{HaveBlock, RefWindow};
42use std::{
43 collections::{hash_map::Entry, HashMap},
44 fmt,
45};
46
47const LOG_TARGET: &str = "state-db";
48const LOG_TARGET_PIN: &str = "state-db::pin";
49const PRUNING_MODE: &[u8] = b"mode";
50const PRUNING_MODE_ARCHIVE: &[u8] = b"archive";
51const PRUNING_MODE_ARCHIVE_CANON: &[u8] = b"archive_canonical";
52const PRUNING_MODE_CONSTRAINED: &[u8] = b"constrained";
53pub(crate) const DEFAULT_MAX_BLOCK_CONSTRAINT: u32 = 256;
54
55pub type DBValue = Vec<u8>;
57
58pub trait Hash:
60 Send
61 + Sync
62 + Sized
63 + Eq
64 + PartialEq
65 + Clone
66 + Default
67 + fmt::Debug
68 + Codec
69 + std::hash::Hash
70 + 'static
71{
72}
73impl<
74 T: Send
75 + Sync
76 + Sized
77 + Eq
78 + PartialEq
79 + Clone
80 + Default
81 + fmt::Debug
82 + Codec
83 + std::hash::Hash
84 + 'static,
85 > Hash for T
86{
87}
88
89pub trait MetaDb {
91 type Error: fmt::Debug;
92
93 fn get_meta(&self, key: &[u8]) -> Result<Option<DBValue>, Self::Error>;
95}
96
97pub trait NodeDb {
99 type Key: ?Sized;
100 type Error: fmt::Debug;
101
102 fn get(&self, key: &Self::Key) -> Result<Option<DBValue>, Self::Error>;
104}
105
106#[derive(Eq, PartialEq)]
108pub enum Error<E> {
109 Db(E),
111 StateDb(StateDbError),
112}
113
114#[derive(Eq, PartialEq)]
115pub enum StateDbError {
116 Decoding(codec::Error),
118 InvalidBlock,
120 InvalidBlockNumber,
122 InvalidParent,
124 IncompatiblePruningModes { stored: PruningMode, requested: PruningMode },
126 TooManySiblingBlocks { number: u64 },
128 BlockAlreadyExists,
130 Metadata(String),
132 BlockUnavailable,
134 BlockMissing,
136}
137
138impl<E> From<StateDbError> for Error<E> {
139 fn from(inner: StateDbError) -> Self {
140 Self::StateDb(inner)
141 }
142}
143
144#[derive(Debug)]
146pub enum PinError {
147 InvalidBlock,
149}
150
151impl<E: fmt::Debug> From<codec::Error> for Error<E> {
152 fn from(x: codec::Error) -> Self {
153 StateDbError::Decoding(x).into()
154 }
155}
156
157impl<E: fmt::Debug> fmt::Debug for Error<E> {
158 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159 match self {
160 Self::Db(e) => e.fmt(f),
161 Self::StateDb(e) => e.fmt(f),
162 }
163 }
164}
165
166impl fmt::Debug for StateDbError {
167 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168 match self {
169 Self::Decoding(e) => write!(f, "Error decoding sliceable value: {}", e),
170 Self::InvalidBlock => write!(f, "Trying to canonicalize invalid block"),
171 Self::InvalidBlockNumber => write!(f, "Trying to insert block with invalid number"),
172 Self::InvalidParent => write!(f, "Trying to insert block with unknown parent"),
173 Self::IncompatiblePruningModes { stored, requested } => write!(
174 f,
175 "Incompatible pruning modes [stored: {:?}; requested: {:?}]",
176 stored, requested
177 ),
178 Self::TooManySiblingBlocks { number } => {
179 write!(f, "Too many sibling blocks at #{number} inserted")
180 },
181 Self::BlockAlreadyExists => write!(f, "Block already exists"),
182 Self::Metadata(message) => write!(f, "Invalid metadata: {}", message),
183 Self::BlockUnavailable => {
184 write!(f, "Trying to get a block record from db while it is not commit to db yet")
185 },
186 Self::BlockMissing => write!(f, "Block record is missing from the pruning window"),
187 }
188 }
189}
190
191#[derive(Default, Debug, Clone)]
193pub struct ChangeSet<H: Hash> {
194 pub inserted: Vec<(H, DBValue)>,
196 pub deleted: Vec<H>,
198}
199
200#[derive(Default, Debug, Clone)]
202pub struct CommitSet<H: Hash> {
203 pub data: ChangeSet<H>,
205 pub meta: ChangeSet<Vec<u8>>,
207}
208
209#[derive(Debug, Clone, Eq, PartialEq)]
211pub struct Constraints {
212 pub max_blocks: Option<u32>,
215}
216
217#[derive(Debug, Clone, Eq, PartialEq)]
219pub enum PruningMode {
220 Constrained(Constraints),
222 ArchiveAll,
224 ArchiveCanonical,
226}
227
228impl PruningMode {
229 pub fn blocks_pruning(n: u32) -> PruningMode {
231 PruningMode::Constrained(Constraints { max_blocks: Some(n) })
232 }
233
234 pub fn is_archive(&self) -> bool {
236 match *self {
237 PruningMode::ArchiveAll | PruningMode::ArchiveCanonical => true,
238 PruningMode::Constrained(_) => false,
239 }
240 }
241
242 pub fn id(&self) -> &[u8] {
244 match self {
245 PruningMode::ArchiveAll => PRUNING_MODE_ARCHIVE,
246 PruningMode::ArchiveCanonical => PRUNING_MODE_ARCHIVE_CANON,
247 PruningMode::Constrained(_) => PRUNING_MODE_CONSTRAINED,
248 }
249 }
250
251 pub fn from_id(id: &[u8]) -> Option<Self> {
252 match id {
253 PRUNING_MODE_ARCHIVE => Some(Self::ArchiveAll),
254 PRUNING_MODE_ARCHIVE_CANON => Some(Self::ArchiveCanonical),
255 PRUNING_MODE_CONSTRAINED => Some(Self::Constrained(Default::default())),
256 _ => None,
257 }
258 }
259}
260
261impl Default for PruningMode {
262 fn default() -> Self {
263 PruningMode::Constrained(Default::default())
264 }
265}
266
267impl Default for Constraints {
268 fn default() -> Self {
269 Self { max_blocks: Some(DEFAULT_MAX_BLOCK_CONSTRAINT) }
270 }
271}
272
273fn to_meta_key<S: Codec>(suffix: &[u8], data: &S) -> Vec<u8> {
274 let mut buffer = data.encode();
275 buffer.extend(suffix);
276 buffer
277}
278
279#[derive(Clone, Debug, PartialEq, Eq)]
281pub enum LastCanonicalized {
282 None,
284 Block(u64),
286 NotCanonicalizing,
288}
289
290pub struct StateDbSync<BlockHash: Hash, Key: Hash, D: MetaDb> {
291 mode: PruningMode,
292 non_canonical: NonCanonicalOverlay<BlockHash, Key>,
293 pruning: Option<RefWindow<BlockHash, Key, D>>,
294 pinned: HashMap<BlockHash, u32>,
295 ref_counting: bool,
296}
297
298impl<BlockHash: Hash, Key: Hash, D: MetaDb> StateDbSync<BlockHash, Key, D> {
299 fn new(
300 mode: PruningMode,
301 ref_counting: bool,
302 db: D,
303 ) -> Result<StateDbSync<BlockHash, Key, D>, Error<D::Error>> {
304 trace!(target: LOG_TARGET, "StateDb settings: {:?}. Ref-counting: {}", mode, ref_counting);
305
306 let non_canonical: NonCanonicalOverlay<BlockHash, Key> = NonCanonicalOverlay::new(&db)?;
307 let pruning: Option<RefWindow<BlockHash, Key, D>> = match mode {
308 PruningMode::Constrained(Constraints { max_blocks }) => {
309 Some(RefWindow::new(db, max_blocks.unwrap_or(0), ref_counting)?)
310 },
311 PruningMode::ArchiveAll | PruningMode::ArchiveCanonical => None,
312 };
313
314 Ok(StateDbSync { mode, non_canonical, pruning, pinned: Default::default(), ref_counting })
315 }
316
317 fn insert_block(
318 &mut self,
319 hash: &BlockHash,
320 number: u64,
321 parent_hash: &BlockHash,
322 mut changeset: ChangeSet<Key>,
323 ) -> Result<CommitSet<Key>, Error<D::Error>> {
324 match self.mode {
325 PruningMode::ArchiveAll => {
326 changeset.deleted.clear();
327 Ok(CommitSet { data: changeset, meta: Default::default() })
329 },
330 PruningMode::Constrained(_) | PruningMode::ArchiveCanonical => self
331 .non_canonical
332 .insert(hash, number, parent_hash, changeset)
333 .map_err(Into::into),
334 }
335 }
336
337 fn canonicalize_block(&mut self, hash: &BlockHash) -> Result<CommitSet<Key>, Error<D::Error>> {
338 let mut commit = CommitSet::default();
343 if self.mode == PruningMode::ArchiveAll {
344 return Ok(commit);
345 }
346 let number = self.non_canonical.canonicalize(hash, &mut commit)?;
347 if self.mode == PruningMode::ArchiveCanonical {
348 commit.data.deleted.clear();
349 }
350 if let Some(ref mut pruning) = self.pruning {
351 pruning.note_canonical(hash, number, &mut commit)?;
352 }
353 self.prune(&mut commit)?;
354 Ok(commit)
355 }
356
357 fn last_canonicalized(&self) -> LastCanonicalized {
359 if self.mode == PruningMode::ArchiveAll {
360 LastCanonicalized::NotCanonicalizing
361 } else {
362 self.non_canonical
363 .last_canonicalized_block_number()
364 .map(LastCanonicalized::Block)
365 .unwrap_or_else(|| LastCanonicalized::None)
366 }
367 }
368
369 fn is_pruned(&self, hash: &BlockHash, number: u64) -> IsPruned {
370 match self.mode {
371 PruningMode::ArchiveAll => IsPruned::NotPruned,
372 PruningMode::ArchiveCanonical | PruningMode::Constrained(_) => {
373 if self
374 .non_canonical
375 .last_canonicalized_block_number()
376 .map(|c| number > c)
377 .unwrap_or(true)
378 {
379 if self.non_canonical.have_block(hash) {
380 IsPruned::NotPruned
381 } else {
382 IsPruned::Pruned
383 }
384 } else {
385 match self.pruning.as_ref() {
386 None => IsPruned::MaybePruned,
388 Some(pruning) => match pruning.have_block(hash, number) {
389 HaveBlock::No => IsPruned::Pruned,
390 HaveBlock::Yes => IsPruned::NotPruned,
391 HaveBlock::Maybe => IsPruned::MaybePruned,
392 },
393 }
394 }
395 },
396 }
397 }
398
399 fn prune(&mut self, commit: &mut CommitSet<Key>) -> Result<(), Error<D::Error>> {
400 if let (&mut Some(ref mut pruning), PruningMode::Constrained(constraints)) =
401 (&mut self.pruning, &self.mode)
402 {
403 loop {
404 if pruning.window_size() <= constraints.max_blocks.unwrap_or(0) as u64 {
405 break;
406 }
407
408 let pinned = &self.pinned;
409 match pruning.next_hash() {
410 Err(Error::StateDb(StateDbError::BlockUnavailable)) => break,
412 res => {
413 if res?.map_or(false, |h| pinned.contains_key(&h)) {
414 break;
415 }
416 },
417 }
418 match pruning.prune_one(commit) {
419 Err(Error::StateDb(StateDbError::BlockUnavailable)) => break,
422 res => res?,
423 }
424 }
425 }
426 Ok(())
427 }
428
429 fn revert_one(&mut self) -> Option<CommitSet<Key>> {
433 match self.mode {
434 PruningMode::ArchiveAll => Some(CommitSet::default()),
435 PruningMode::ArchiveCanonical | PruningMode::Constrained(_) => {
436 self.non_canonical.revert_one()
437 },
438 }
439 }
440
441 fn remove(&mut self, hash: &BlockHash) -> Option<CommitSet<Key>> {
442 match self.mode {
443 PruningMode::ArchiveAll => Some(CommitSet::default()),
444 PruningMode::ArchiveCanonical | PruningMode::Constrained(_) => {
445 self.non_canonical.remove(hash)
446 },
447 }
448 }
449
450 fn pin<F>(&mut self, hash: &BlockHash, number: u64, hint: F) -> Result<(), PinError>
451 where
452 F: Fn() -> bool,
453 {
454 match self.mode {
455 PruningMode::ArchiveAll => Ok(()),
456 PruningMode::ArchiveCanonical | PruningMode::Constrained(_) => {
457 let have_block = self.non_canonical.have_block(hash)
458 || self.pruning.as_ref().map_or_else(
459 || hint(),
460 |pruning| match pruning.have_block(hash, number) {
461 HaveBlock::No => false,
462 HaveBlock::Yes => true,
463 HaveBlock::Maybe => hint(),
464 },
465 );
466 if have_block {
467 let refs = self.pinned.entry(hash.clone()).or_default();
468 if *refs == 0 {
469 trace!(target: LOG_TARGET_PIN, "Pinned block: {:?}", hash);
470 self.non_canonical.pin(hash);
471 }
472 *refs += 1;
473 Ok(())
474 } else {
475 Err(PinError::InvalidBlock)
476 }
477 },
478 }
479 }
480
481 fn unpin(&mut self, hash: &BlockHash) {
482 match self.pinned.entry(hash.clone()) {
483 Entry::Occupied(mut entry) => {
484 *entry.get_mut() -= 1;
485 if *entry.get() == 0 {
486 trace!(target: LOG_TARGET_PIN, "Unpinned block: {:?}", hash);
487 entry.remove();
488 self.non_canonical.unpin(hash);
489 } else {
490 trace!(target: LOG_TARGET_PIN, "Releasing reference for {:?}", hash);
491 }
492 },
493 Entry::Vacant(_) => {},
494 }
495 }
496
497 fn sync(&mut self) {
498 self.non_canonical.sync();
499 }
500
501 pub fn get<DB: NodeDb, Q: ?Sized>(
502 &self,
503 key: &Q,
504 db: &DB,
505 ) -> Result<Option<DBValue>, Error<DB::Error>>
506 where
507 Q: AsRef<DB::Key>,
508 Key: std::borrow::Borrow<Q>,
509 Q: std::hash::Hash + Eq,
510 {
511 if let Some(value) = self.non_canonical.get(key) {
512 return Ok(Some(value));
513 }
514 db.get(key.as_ref()).map_err(Error::Db)
515 }
516}
517
518pub struct StateDb<BlockHash: Hash, Key: Hash, D: MetaDb> {
521 db: RwLock<StateDbSync<BlockHash, Key, D>>,
522}
523
524impl<BlockHash: Hash, Key: Hash, D: MetaDb> StateDb<BlockHash, Key, D> {
525 pub fn open(
527 db: D,
528 requested_mode: Option<PruningMode>,
529 ref_counting: bool,
530 should_init: bool,
531 ) -> Result<(CommitSet<Key>, StateDb<BlockHash, Key, D>), Error<D::Error>> {
532 let stored_mode = fetch_stored_pruning_mode(&db)?;
533
534 let selected_mode = match (should_init, stored_mode, requested_mode) {
535 (true, stored_mode, requested_mode) => {
536 assert!(stored_mode.is_none(), "The storage has just been initialized. No meta-data is expected to be found in it.");
537 requested_mode.unwrap_or_default()
538 },
539
540 (false, None, _) => {
541 return Err(StateDbError::Metadata(
542 "An existing StateDb does not have PRUNING_MODE stored in its meta-data".into(),
543 )
544 .into())
545 },
546
547 (false, Some(stored), None) => stored,
548
549 (false, Some(stored), Some(requested)) => choose_pruning_mode(stored, requested)?,
550 };
551
552 let db_init_commit_set = if should_init {
553 let mut cs: CommitSet<Key> = Default::default();
554
555 let key = to_meta_key(PRUNING_MODE, &());
556 let value = selected_mode.id().to_owned();
557
558 cs.meta.inserted.push((key, value));
559
560 cs
561 } else {
562 Default::default()
563 };
564
565 let state_db =
566 StateDb { db: RwLock::new(StateDbSync::new(selected_mode, ref_counting, db)?) };
567
568 Ok((db_init_commit_set, state_db))
569 }
570
571 pub fn pruning_mode(&self) -> PruningMode {
572 self.db.read().mode.clone()
573 }
574
575 pub fn insert_block(
577 &self,
578 hash: &BlockHash,
579 number: u64,
580 parent_hash: &BlockHash,
581 changeset: ChangeSet<Key>,
582 ) -> Result<CommitSet<Key>, Error<D::Error>> {
583 self.db.write().insert_block(hash, number, parent_hash, changeset)
584 }
585
586 pub fn canonicalize_block(&self, hash: &BlockHash) -> Result<CommitSet<Key>, Error<D::Error>> {
588 self.db.write().canonicalize_block(hash)
589 }
590
591 pub fn pin<F>(&self, hash: &BlockHash, number: u64, hint: F) -> Result<(), PinError>
594 where
595 F: Fn() -> bool,
596 {
597 self.db.write().pin(hash, number, hint)
598 }
599
600 pub fn unpin(&self, hash: &BlockHash) {
602 self.db.write().unpin(hash)
603 }
604
605 pub fn sync(&self) {
608 self.db.write().sync()
609 }
610
611 pub fn get<DB: NodeDb, Q: ?Sized>(
613 &self,
614 key: &Q,
615 db: &DB,
616 ) -> Result<Option<DBValue>, Error<DB::Error>>
617 where
618 Q: AsRef<DB::Key>,
619 Key: std::borrow::Borrow<Q>,
620 Q: std::hash::Hash + Eq,
621 {
622 self.db.read().get(key, db)
623 }
624
625 pub fn revert_one(&self) -> Option<CommitSet<Key>> {
629 self.db.write().revert_one()
630 }
631
632 pub fn remove(&self, hash: &BlockHash) -> Option<CommitSet<Key>> {
635 self.db.write().remove(hash)
636 }
637
638 pub fn last_canonicalized(&self) -> LastCanonicalized {
640 self.db.read().last_canonicalized()
641 }
642
643 pub fn is_pruned(&self, hash: &BlockHash, number: u64) -> IsPruned {
645 self.db.read().is_pruned(hash, number)
646 }
647
648 pub fn reset(&self, db: D) -> Result<(), Error<D::Error>> {
650 let mut state_db = self.db.write();
651 *state_db = StateDbSync::new(state_db.mode.clone(), state_db.ref_counting, db)?;
652 Ok(())
653 }
654}
655
656#[derive(Debug, PartialEq, Eq)]
658pub enum IsPruned {
659 Pruned,
661 NotPruned,
663 MaybePruned,
665}
666
667fn fetch_stored_pruning_mode<D: MetaDb>(db: &D) -> Result<Option<PruningMode>, Error<D::Error>> {
668 let meta_key_mode = to_meta_key(PRUNING_MODE, &());
669 if let Some(stored_mode) = db.get_meta(&meta_key_mode).map_err(Error::Db)? {
670 if let Some(mode) = PruningMode::from_id(&stored_mode) {
671 Ok(Some(mode))
672 } else {
673 Err(StateDbError::Metadata(format!(
674 "Invalid value stored for PRUNING_MODE: {:02x?}",
675 stored_mode
676 ))
677 .into())
678 }
679 } else {
680 Ok(None)
681 }
682}
683
684fn choose_pruning_mode(
685 stored: PruningMode,
686 requested: PruningMode,
687) -> Result<PruningMode, StateDbError> {
688 match (stored, requested) {
689 (PruningMode::ArchiveAll, PruningMode::ArchiveAll) => Ok(PruningMode::ArchiveAll),
690 (PruningMode::ArchiveCanonical, PruningMode::ArchiveCanonical) => {
691 Ok(PruningMode::ArchiveCanonical)
692 },
693 (PruningMode::Constrained(_), PruningMode::Constrained(requested)) => {
694 Ok(PruningMode::Constrained(requested))
695 },
696 (stored, requested) => Err(StateDbError::IncompatiblePruningModes { requested, stored }),
697 }
698}
699
700#[cfg(test)]
701mod tests {
702 use crate::db::state_db::{
703 test::{make_changeset, make_db, TestDb},
704 Constraints, Error, IsPruned, PruningMode, StateDb, StateDbError,
705 };
706 use subsoil::core::H256;
707
708 fn make_test_db(settings: PruningMode) -> (TestDb, StateDb<H256, H256, TestDb>) {
709 let mut db = make_db(&[91, 921, 922, 93, 94]);
710 let (state_db_init, state_db) =
711 StateDb::open(db.clone(), Some(settings), false, true).unwrap();
712 db.commit(&state_db_init);
713
714 db.commit(
715 &state_db
716 .insert_block(
717 &H256::from_low_u64_be(1),
718 1,
719 &H256::from_low_u64_be(0),
720 make_changeset(&[1], &[91]),
721 )
722 .unwrap(),
723 );
724 db.commit(
725 &state_db
726 .insert_block(
727 &H256::from_low_u64_be(21),
728 2,
729 &H256::from_low_u64_be(1),
730 make_changeset(&[21], &[921, 1]),
731 )
732 .unwrap(),
733 );
734 db.commit(
735 &state_db
736 .insert_block(
737 &H256::from_low_u64_be(22),
738 2,
739 &H256::from_low_u64_be(1),
740 make_changeset(&[22], &[922]),
741 )
742 .unwrap(),
743 );
744 db.commit(
745 &state_db
746 .insert_block(
747 &H256::from_low_u64_be(3),
748 3,
749 &H256::from_low_u64_be(21),
750 make_changeset(&[3], &[93]),
751 )
752 .unwrap(),
753 );
754 db.commit(&state_db.canonicalize_block(&H256::from_low_u64_be(1)).unwrap());
755 db.commit(
756 &state_db
757 .insert_block(
758 &H256::from_low_u64_be(4),
759 4,
760 &H256::from_low_u64_be(3),
761 make_changeset(&[4], &[94]),
762 )
763 .unwrap(),
764 );
765 db.commit(&state_db.canonicalize_block(&H256::from_low_u64_be(21)).unwrap());
766 db.commit(&state_db.canonicalize_block(&H256::from_low_u64_be(3)).unwrap());
767
768 (db, state_db)
769 }
770
771 #[test]
772 fn full_archive_keeps_everything() {
773 let (db, sdb) = make_test_db(PruningMode::ArchiveAll);
774 assert!(db.data_eq(&make_db(&[1, 21, 22, 3, 4, 91, 921, 922, 93, 94])));
775 assert_eq!(sdb.is_pruned(&H256::from_low_u64_be(0), 0), IsPruned::NotPruned);
776 }
777
778 #[test]
779 fn canonical_archive_keeps_canonical() {
780 let (db, _) = make_test_db(PruningMode::ArchiveCanonical);
781 assert!(db.data_eq(&make_db(&[1, 21, 3, 91, 921, 922, 93, 94])));
782 }
783
784 #[test]
785 fn block_record_unavailable() {
786 let (mut db, state_db) =
787 make_test_db(PruningMode::Constrained(Constraints { max_blocks: Some(1) }));
788 for i in &[5, 6] {
790 db.commit(
791 &state_db
792 .insert_block(
793 &H256::from_low_u64_be(*i),
794 *i,
795 &H256::from_low_u64_be(*i - 1),
796 make_changeset(&[], &[]),
797 )
798 .unwrap(),
799 );
800 }
801 let c1 = state_db.canonicalize_block(&H256::from_low_u64_be(4)).unwrap();
803 assert_eq!(state_db.is_pruned(&H256::from_low_u64_be(3), 3), IsPruned::Pruned);
804
805 let c2 = state_db.canonicalize_block(&H256::from_low_u64_be(5)).unwrap();
809 assert_eq!(state_db.is_pruned(&H256::from_low_u64_be(4), 4), IsPruned::MaybePruned);
810
811 db.commit(&c1);
813 db.commit(&c2);
814 db.commit(&state_db.canonicalize_block(&H256::from_low_u64_be(6)).unwrap());
815 assert_eq!(state_db.is_pruned(&H256::from_low_u64_be(4), 4), IsPruned::Pruned);
816 assert_eq!(state_db.is_pruned(&H256::from_low_u64_be(5), 5), IsPruned::Pruned);
817 }
818
819 #[test]
820 fn prune_window_0() {
821 let (db, _) = make_test_db(PruningMode::Constrained(Constraints { max_blocks: Some(0) }));
822 assert!(db.data_eq(&make_db(&[21, 3, 922, 94])));
823 }
824
825 #[test]
826 fn prune_window_1() {
827 let (db, sdb) = make_test_db(PruningMode::Constrained(Constraints { max_blocks: Some(1) }));
828 assert_eq!(sdb.is_pruned(&H256::from_low_u64_be(0), 0), IsPruned::Pruned);
829 assert_eq!(sdb.is_pruned(&H256::from_low_u64_be(1), 1), IsPruned::Pruned);
830 assert_eq!(sdb.is_pruned(&H256::from_low_u64_be(21), 2), IsPruned::Pruned);
831 assert_eq!(sdb.is_pruned(&H256::from_low_u64_be(22), 2), IsPruned::Pruned);
832 assert!(db.data_eq(&make_db(&[21, 3, 922, 93, 94])));
833 }
834
835 #[test]
836 fn prune_window_2() {
837 let (db, sdb) = make_test_db(PruningMode::Constrained(Constraints { max_blocks: Some(2) }));
838 assert_eq!(sdb.is_pruned(&H256::from_low_u64_be(0), 0), IsPruned::Pruned);
839 assert_eq!(sdb.is_pruned(&H256::from_low_u64_be(1), 1), IsPruned::Pruned);
840 assert_eq!(sdb.is_pruned(&H256::from_low_u64_be(21), 2), IsPruned::NotPruned);
841 assert_eq!(sdb.is_pruned(&H256::from_low_u64_be(22), 2), IsPruned::Pruned);
842 assert!(db.data_eq(&make_db(&[1, 21, 3, 921, 922, 93, 94])));
843 }
844
845 #[test]
846 fn detects_incompatible_mode() {
847 let mut db = make_db(&[]);
848 let (state_db_init, state_db) =
849 StateDb::open(db.clone(), Some(PruningMode::ArchiveAll), false, true).unwrap();
850 db.commit(&state_db_init);
851 db.commit(
852 &state_db
853 .insert_block(
854 &H256::from_low_u64_be(0),
855 0,
856 &H256::from_low_u64_be(0),
857 make_changeset(&[], &[]),
858 )
859 .unwrap(),
860 );
861 let new_mode = PruningMode::Constrained(Constraints { max_blocks: Some(2) });
862 let state_db_open_result: Result<(_, StateDb<H256, H256, TestDb>), _> =
863 StateDb::open(db.clone(), Some(new_mode), false, false);
864 assert!(state_db_open_result.is_err());
865 }
866
867 fn check_stored_and_requested_mode_compatibility(
868 mode_when_created: Option<PruningMode>,
869 mode_when_reopened: Option<PruningMode>,
870 expected_effective_mode_when_reopened: Result<PruningMode, ()>,
871 ) {
872 let mut db = make_db(&[]);
873 let (state_db_init, state_db) =
874 StateDb::<H256, H256, TestDb>::open(db.clone(), mode_when_created, false, true)
875 .unwrap();
876 db.commit(&state_db_init);
877 std::mem::drop(state_db);
878
879 let state_db_reopen_result =
880 StateDb::<H256, H256, TestDb>::open(db.clone(), mode_when_reopened, false, false);
881 if let Ok(expected_mode) = expected_effective_mode_when_reopened {
882 let (state_db_init, state_db_reopened) = state_db_reopen_result.unwrap();
883 db.commit(&state_db_init);
884 assert_eq!(state_db_reopened.pruning_mode(), expected_mode,)
885 } else {
886 assert!(matches!(
887 state_db_reopen_result,
888 Err(Error::StateDb(StateDbError::IncompatiblePruningModes { .. }))
889 ));
890 }
891 }
892
893 #[test]
894 fn pruning_mode_compatibility() {
895 for (created, reopened, expected) in [
896 (None, None, Ok(PruningMode::blocks_pruning(256))),
897 (None, Some(PruningMode::blocks_pruning(256)), Ok(PruningMode::blocks_pruning(256))),
898 (None, Some(PruningMode::blocks_pruning(128)), Ok(PruningMode::blocks_pruning(128))),
899 (None, Some(PruningMode::blocks_pruning(512)), Ok(PruningMode::blocks_pruning(512))),
900 (None, Some(PruningMode::ArchiveAll), Err(())),
901 (None, Some(PruningMode::ArchiveCanonical), Err(())),
902 (Some(PruningMode::blocks_pruning(256)), None, Ok(PruningMode::blocks_pruning(256))),
903 (
904 Some(PruningMode::blocks_pruning(256)),
905 Some(PruningMode::blocks_pruning(256)),
906 Ok(PruningMode::blocks_pruning(256)),
907 ),
908 (
909 Some(PruningMode::blocks_pruning(256)),
910 Some(PruningMode::blocks_pruning(128)),
911 Ok(PruningMode::blocks_pruning(128)),
912 ),
913 (
914 Some(PruningMode::blocks_pruning(256)),
915 Some(PruningMode::blocks_pruning(512)),
916 Ok(PruningMode::blocks_pruning(512)),
917 ),
918 (Some(PruningMode::blocks_pruning(256)), Some(PruningMode::ArchiveAll), Err(())),
919 (Some(PruningMode::blocks_pruning(256)), Some(PruningMode::ArchiveCanonical), Err(())),
920 (Some(PruningMode::ArchiveAll), None, Ok(PruningMode::ArchiveAll)),
921 (Some(PruningMode::ArchiveAll), Some(PruningMode::blocks_pruning(256)), Err(())),
922 (Some(PruningMode::ArchiveAll), Some(PruningMode::blocks_pruning(128)), Err(())),
923 (Some(PruningMode::ArchiveAll), Some(PruningMode::blocks_pruning(512)), Err(())),
924 (
925 Some(PruningMode::ArchiveAll),
926 Some(PruningMode::ArchiveAll),
927 Ok(PruningMode::ArchiveAll),
928 ),
929 (Some(PruningMode::ArchiveAll), Some(PruningMode::ArchiveCanonical), Err(())),
930 (Some(PruningMode::ArchiveCanonical), None, Ok(PruningMode::ArchiveCanonical)),
931 (Some(PruningMode::ArchiveCanonical), Some(PruningMode::blocks_pruning(256)), Err(())),
932 (Some(PruningMode::ArchiveCanonical), Some(PruningMode::blocks_pruning(128)), Err(())),
933 (Some(PruningMode::ArchiveCanonical), Some(PruningMode::blocks_pruning(512)), Err(())),
934 (Some(PruningMode::ArchiveCanonical), Some(PruningMode::ArchiveAll), Err(())),
935 (
936 Some(PruningMode::ArchiveCanonical),
937 Some(PruningMode::ArchiveCanonical),
938 Ok(PruningMode::ArchiveCanonical),
939 ),
940 ] {
941 check_stored_and_requested_mode_compatibility(created, reopened, expected);
942 }
943 }
944}