1pub mod migration;
22
23use codec::{Decode, Encode};
24use pez_fork_tree::{FilterAction, ForkTree};
25use pezsc_client_api::utils::is_descendent_of;
26use pezsp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata};
27use pezsp_runtime::traits::{Block as BlockT, NumberFor, One, Zero};
28use std::{
29 borrow::{Borrow, BorrowMut},
30 collections::BTreeMap,
31 ops::{Add, Sub},
32};
33
34pub trait IsDescendentOfBuilder<Hash> {
36 type Error: std::error::Error;
38 type IsDescendentOf: Fn(&Hash, &Hash) -> Result<bool, Self::Error>;
41
42 fn build_is_descendent_of(&self, current: Option<(Hash, Hash)>) -> Self::IsDescendentOf;
49}
50
51pub fn descendent_query<H, Block>(client: &H) -> HeaderBackendDescendentBuilder<&H, Block> {
53 HeaderBackendDescendentBuilder(client, std::marker::PhantomData)
54}
55
56pub struct HeaderBackendDescendentBuilder<H, Block>(H, std::marker::PhantomData<Block>);
59
60impl<'a, H, Block> IsDescendentOfBuilder<Block::Hash>
61 for HeaderBackendDescendentBuilder<&'a H, Block>
62where
63 H: HeaderBackend<Block> + HeaderMetadata<Block, Error = ClientError>,
64 Block: BlockT,
65{
66 type Error = ClientError;
67 type IsDescendentOf = Box<dyn Fn(&Block::Hash, &Block::Hash) -> Result<bool, ClientError> + 'a>;
68
69 fn build_is_descendent_of(
70 &self,
71 current: Option<(Block::Hash, Block::Hash)>,
72 ) -> Self::IsDescendentOf {
73 Box::new(is_descendent_of(self.0, current))
74 }
75}
76
77pub trait Epoch: std::fmt::Debug {
82 type NextEpochDescriptor;
84 type Slot: Ord + Copy + std::fmt::Debug;
86
87 fn start_slot(&self) -> Self::Slot;
89 fn end_slot(&self) -> Self::Slot;
92 fn increment(&self, descriptor: Self::NextEpochDescriptor) -> Self;
94}
95
96impl<'a, E: Epoch> From<&'a E> for EpochHeader<E> {
97 fn from(epoch: &'a E) -> EpochHeader<E> {
98 Self { start_slot: epoch.start_slot(), end_slot: epoch.end_slot() }
99 }
100}
101
102#[derive(Eq, PartialEq, Encode, Decode, Debug)]
104pub struct EpochHeader<E: Epoch> {
105 pub start_slot: E::Slot,
107 pub end_slot: E::Slot,
110}
111
112impl<E: Epoch> Clone for EpochHeader<E> {
113 fn clone(&self) -> Self {
114 Self { start_slot: self.start_slot, end_slot: self.end_slot }
115 }
116}
117
118#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Debug)]
120pub enum EpochIdentifierPosition {
121 Genesis0,
123 Genesis1,
125 Regular,
127}
128
129#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
131pub struct EpochIdentifier<Hash, Number> {
132 pub position: EpochIdentifierPosition,
134 pub hash: Hash,
136 pub number: Number,
138}
139
140pub enum ViableEpoch<E, ERef = E> {
145 UnimportedGenesis(E),
147 Signaled(ERef),
149}
150
151impl<E, ERef> AsRef<E> for ViableEpoch<E, ERef>
152where
153 ERef: Borrow<E>,
154{
155 fn as_ref(&self) -> &E {
156 match *self {
157 ViableEpoch::UnimportedGenesis(ref e) => e,
158 ViableEpoch::Signaled(ref e) => e.borrow(),
159 }
160 }
161}
162
163impl<E, ERef> AsMut<E> for ViableEpoch<E, ERef>
164where
165 ERef: BorrowMut<E>,
166{
167 fn as_mut(&mut self) -> &mut E {
168 match *self {
169 ViableEpoch::UnimportedGenesis(ref mut e) => e,
170 ViableEpoch::Signaled(ref mut e) => e.borrow_mut(),
171 }
172 }
173}
174
175impl<E, ERef> ViableEpoch<E, ERef>
176where
177 E: Epoch + Clone,
178 ERef: Borrow<E>,
179{
180 pub fn into_cloned_inner(self) -> E {
183 match self {
184 ViableEpoch::UnimportedGenesis(e) => e,
185 ViableEpoch::Signaled(e) => e.borrow().clone(),
186 }
187 }
188
189 pub fn into_cloned(self) -> ViableEpoch<E, E> {
191 match self {
192 ViableEpoch::UnimportedGenesis(e) => ViableEpoch::UnimportedGenesis(e),
193 ViableEpoch::Signaled(e) => ViableEpoch::Signaled(e.borrow().clone()),
194 }
195 }
196
197 pub fn increment(&self, next_descriptor: E::NextEpochDescriptor) -> IncrementedEpoch<E> {
200 let next = self.as_ref().increment(next_descriptor);
201 let to_persist = match *self {
202 ViableEpoch::UnimportedGenesis(ref epoch_0) => {
203 PersistedEpoch::Genesis(epoch_0.clone(), next)
204 },
205 ViableEpoch::Signaled(_) => PersistedEpoch::Regular(next),
206 };
207
208 IncrementedEpoch(to_persist)
209 }
210}
211
212#[derive(PartialEq, Eq, Clone, Debug)]
214pub enum ViableEpochDescriptor<Hash, Number, E: Epoch> {
215 UnimportedGenesis(E::Slot),
217 Signaled(EpochIdentifier<Hash, Number>, EpochHeader<E>),
219}
220
221impl<Hash, Number, E: Epoch> ViableEpochDescriptor<Hash, Number, E> {
222 pub fn start_slot(&self) -> E::Slot {
224 match self {
225 Self::UnimportedGenesis(start_slot) => *start_slot,
226 Self::Signaled(_, header) => header.start_slot,
227 }
228 }
229}
230
231#[derive(Clone, Encode, Decode, Debug)]
233pub enum PersistedEpoch<E> {
234 Genesis(E, E),
236 Regular(E),
238}
239
240impl<E> PersistedEpoch<E> {
241 pub fn is_genesis(&self) -> bool {
243 matches!(self, Self::Genesis(_, _))
244 }
245}
246
247impl<'a, E: Epoch> From<&'a PersistedEpoch<E>> for PersistedEpochHeader<E> {
248 fn from(epoch: &'a PersistedEpoch<E>) -> Self {
249 match epoch {
250 PersistedEpoch::Genesis(ref epoch_0, ref epoch_1) => {
251 PersistedEpochHeader::Genesis(epoch_0.into(), epoch_1.into())
252 },
253 PersistedEpoch::Regular(ref epoch_n) => PersistedEpochHeader::Regular(epoch_n.into()),
254 }
255 }
256}
257
258impl<E: Epoch> PersistedEpoch<E> {
259 pub fn map<B, F, Hash, Number>(self, h: &Hash, n: &Number, f: &mut F) -> PersistedEpoch<B>
261 where
262 B: Epoch<Slot = E::Slot>,
263 F: FnMut(&Hash, &Number, E) -> B,
264 {
265 match self {
266 PersistedEpoch::Genesis(epoch_0, epoch_1) => {
267 PersistedEpoch::Genesis(f(h, n, epoch_0), f(h, n, epoch_1))
268 },
269 PersistedEpoch::Regular(epoch_n) => PersistedEpoch::Regular(f(h, n, epoch_n)),
270 }
271 }
272}
273
274#[derive(Encode, Decode, PartialEq, Eq, Debug)]
276pub enum PersistedEpochHeader<E: Epoch> {
277 Genesis(EpochHeader<E>, EpochHeader<E>),
279 Regular(EpochHeader<E>),
281}
282
283impl<E: Epoch> Clone for PersistedEpochHeader<E> {
284 fn clone(&self) -> Self {
285 match self {
286 Self::Genesis(epoch_0, epoch_1) => Self::Genesis(epoch_0.clone(), epoch_1.clone()),
287 Self::Regular(epoch_n) => Self::Regular(epoch_n.clone()),
288 }
289 }
290}
291
292impl<E: Epoch> PersistedEpochHeader<E> {
293 pub fn map<B>(self) -> PersistedEpochHeader<B>
295 where
296 B: Epoch<Slot = E::Slot>,
297 {
298 match self {
299 PersistedEpochHeader::Genesis(epoch_0, epoch_1) => PersistedEpochHeader::Genesis(
300 EpochHeader { start_slot: epoch_0.start_slot, end_slot: epoch_0.end_slot },
301 EpochHeader { start_slot: epoch_1.start_slot, end_slot: epoch_1.end_slot },
302 ),
303 PersistedEpochHeader::Regular(epoch_n) => PersistedEpochHeader::Regular(EpochHeader {
304 start_slot: epoch_n.start_slot,
305 end_slot: epoch_n.end_slot,
306 }),
307 }
308 }
309}
310
311#[must_use = "Freshly-incremented epoch must be imported with `EpochChanges::import`"]
315pub struct IncrementedEpoch<E: Epoch>(PersistedEpoch<E>);
316
317impl<E: Epoch> AsRef<E> for IncrementedEpoch<E> {
318 fn as_ref(&self) -> &E {
319 match self.0 {
320 PersistedEpoch::Genesis(_, ref epoch_1) => epoch_1,
321 PersistedEpoch::Regular(ref epoch_n) => epoch_n,
322 }
323 }
324}
325
326#[derive(Clone, Encode, Decode, Debug)]
342pub struct EpochChanges<Hash, Number, E: Epoch> {
343 inner: ForkTree<Hash, Number, PersistedEpochHeader<E>>,
344 epochs: BTreeMap<(Hash, Number), PersistedEpoch<E>>,
345}
346
347fn fake_head_hash<H: AsRef<[u8]> + AsMut<[u8]> + Clone>(parent_hash: &H) -> H {
349 let mut h = parent_hash.clone();
350 h.as_mut()[0] ^= 0b10000000;
353 h
354}
355
356impl<Hash, Number, E: Epoch> Default for EpochChanges<Hash, Number, E>
357where
358 Hash: PartialEq + Ord,
359 Number: Ord,
360{
361 fn default() -> Self {
362 EpochChanges { inner: ForkTree::new(), epochs: BTreeMap::new() }
363 }
364}
365
366impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E>
367where
368 Hash: PartialEq + Ord + AsRef<[u8]> + AsMut<[u8]> + Copy + std::fmt::Debug,
369 Number: Ord + One + Zero + Add<Output = Number> + Sub<Output = Number> + Copy + std::fmt::Debug,
370{
371 pub fn new() -> Self {
373 Self::default()
374 }
375
376 pub fn rebalance(&mut self) {
379 self.inner.rebalance()
380 }
381
382 pub fn map<B, F>(self, mut f: F) -> EpochChanges<Hash, Number, B>
384 where
385 B: Epoch<Slot = E::Slot>,
386 F: FnMut(&Hash, &Number, E) -> B,
387 {
388 EpochChanges {
389 inner: self.inner.map(&mut |_, _, header: PersistedEpochHeader<E>| header.map()),
390 epochs: self
391 .epochs
392 .into_iter()
393 .map(|((hash, number), epoch)| ((hash, number), epoch.map(&hash, &number, &mut f)))
394 .collect(),
395 }
396 }
397
398 pub fn prune_finalized<D: IsDescendentOfBuilder<Hash>>(
402 &mut self,
403 descendent_of_builder: D,
404 hash: &Hash,
405 number: Number,
406 slot: E::Slot,
407 ) -> Result<(), pez_fork_tree::Error<D::Error>> {
408 let is_descendent_of = descendent_of_builder.build_is_descendent_of(None);
409
410 let predicate = |epoch: &PersistedEpochHeader<E>| match *epoch {
411 PersistedEpochHeader::Genesis(_, ref epoch_1) => epoch_1.start_slot <= slot,
412 PersistedEpochHeader::Regular(ref epoch_n) => epoch_n.start_slot <= slot,
413 };
414
415 let removed = self.inner.prune(hash, &number, &is_descendent_of, &predicate)?;
419
420 for (hash, number, _) in removed {
421 self.epochs.remove(&(hash, number));
422 }
423
424 Ok(())
425 }
426
427 pub fn epoch(&self, id: &EpochIdentifier<Hash, Number>) -> Option<&E> {
429 self.epochs.get(&(id.hash, id.number)).and_then(|v| match v {
430 PersistedEpoch::Genesis(ref epoch_0, _)
431 if id.position == EpochIdentifierPosition::Genesis0 =>
432 {
433 Some(epoch_0)
434 },
435 PersistedEpoch::Genesis(_, ref epoch_1)
436 if id.position == EpochIdentifierPosition::Genesis1 =>
437 {
438 Some(epoch_1)
439 },
440 PersistedEpoch::Regular(ref epoch_n)
441 if id.position == EpochIdentifierPosition::Regular =>
442 {
443 Some(epoch_n)
444 },
445 _ => None,
446 })
447 }
448
449 pub fn viable_epoch<G>(
451 &self,
452 descriptor: &ViableEpochDescriptor<Hash, Number, E>,
453 make_genesis: G,
454 ) -> Option<ViableEpoch<E, &E>>
455 where
456 G: FnOnce(E::Slot) -> E,
457 {
458 match descriptor {
459 ViableEpochDescriptor::UnimportedGenesis(slot) => {
460 Some(ViableEpoch::UnimportedGenesis(make_genesis(*slot)))
461 },
462 ViableEpochDescriptor::Signaled(identifier, _) => {
463 self.epoch(identifier).map(ViableEpoch::Signaled)
464 },
465 }
466 }
467
468 pub fn epoch_mut(&mut self, id: &EpochIdentifier<Hash, Number>) -> Option<&mut E> {
470 self.epochs.get_mut(&(id.hash, id.number)).and_then(|v| match v {
471 PersistedEpoch::Genesis(ref mut epoch_0, _)
472 if id.position == EpochIdentifierPosition::Genesis0 =>
473 {
474 Some(epoch_0)
475 },
476 PersistedEpoch::Genesis(_, ref mut epoch_1)
477 if id.position == EpochIdentifierPosition::Genesis1 =>
478 {
479 Some(epoch_1)
480 },
481 PersistedEpoch::Regular(ref mut epoch_n)
482 if id.position == EpochIdentifierPosition::Regular =>
483 {
484 Some(epoch_n)
485 },
486 _ => None,
487 })
488 }
489
490 pub fn viable_epoch_mut<G>(
492 &mut self,
493 descriptor: &ViableEpochDescriptor<Hash, Number, E>,
494 make_genesis: G,
495 ) -> Option<ViableEpoch<E, &mut E>>
496 where
497 G: FnOnce(E::Slot) -> E,
498 {
499 match descriptor {
500 ViableEpochDescriptor::UnimportedGenesis(slot) => {
501 Some(ViableEpoch::UnimportedGenesis(make_genesis(*slot)))
502 },
503 ViableEpochDescriptor::Signaled(identifier, _) => {
504 self.epoch_mut(identifier).map(ViableEpoch::Signaled)
505 },
506 }
507 }
508
509 pub fn epoch_data<G>(
514 &self,
515 descriptor: &ViableEpochDescriptor<Hash, Number, E>,
516 make_genesis: G,
517 ) -> Option<E>
518 where
519 G: FnOnce(E::Slot) -> E,
520 E: Clone,
521 {
522 match descriptor {
523 ViableEpochDescriptor::UnimportedGenesis(slot) => Some(make_genesis(*slot)),
524 ViableEpochDescriptor::Signaled(identifier, _) => self.epoch(identifier).cloned(),
525 }
526 }
527
528 pub fn epoch_data_for_child_of<D: IsDescendentOfBuilder<Hash>, G>(
534 &self,
535 descendent_of_builder: D,
536 parent_hash: &Hash,
537 parent_number: Number,
538 slot: E::Slot,
539 make_genesis: G,
540 ) -> Result<Option<E>, pez_fork_tree::Error<D::Error>>
541 where
542 G: FnOnce(E::Slot) -> E,
543 E: Clone,
544 {
545 let descriptor = self.epoch_descriptor_for_child_of(
546 descendent_of_builder,
547 parent_hash,
548 parent_number,
549 slot,
550 )?;
551
552 Ok(descriptor.and_then(|des| self.epoch_data(&des, make_genesis)))
553 }
554
555 pub fn epoch_descriptor_for_child_of<D: IsDescendentOfBuilder<Hash>>(
560 &self,
561 descendent_of_builder: D,
562 parent_hash: &Hash,
563 parent_number: Number,
564 slot: E::Slot,
565 ) -> Result<Option<ViableEpochDescriptor<Hash, Number, E>>, pez_fork_tree::Error<D::Error>> {
566 if parent_number == Zero::zero() {
567 return Ok(Some(ViableEpochDescriptor::UnimportedGenesis(slot)));
569 }
570
571 let fake_head_hash = fake_head_hash(parent_hash);
576
577 let is_descendent_of =
578 descendent_of_builder.build_is_descendent_of(Some((fake_head_hash, *parent_hash)));
579
580 let predicate = |epoch: &PersistedEpochHeader<E>| match *epoch {
586 PersistedEpochHeader::Genesis(ref epoch_0, _) => epoch_0.start_slot <= slot,
587 PersistedEpochHeader::Regular(ref epoch_n) => epoch_n.start_slot <= slot,
588 };
589
590 self.inner
591 .find_node_where(
592 &fake_head_hash,
593 &(parent_number + One::one()),
594 &is_descendent_of,
595 &predicate,
596 )
597 .map(|n| {
598 n.map(|node| {
599 (
600 match node.data {
601 PersistedEpochHeader::Genesis(ref epoch_0, ref epoch_1) => {
605 if epoch_1.start_slot <= slot {
606 (EpochIdentifierPosition::Genesis1, epoch_1.clone())
607 } else {
608 (EpochIdentifierPosition::Genesis0, epoch_0.clone())
609 }
610 },
611 PersistedEpochHeader::Regular(ref epoch_n) => {
612 (EpochIdentifierPosition::Regular, epoch_n.clone())
613 },
614 },
615 node,
616 )
617 })
618 .map(|((position, header), node)| {
619 ViableEpochDescriptor::Signaled(
620 EpochIdentifier { position, hash: node.hash, number: node.number },
621 header,
622 )
623 })
624 })
625 }
626
627 pub fn import<D: IsDescendentOfBuilder<Hash>>(
633 &mut self,
634 descendent_of_builder: D,
635 hash: Hash,
636 number: Number,
637 parent_hash: Hash,
638 epoch: IncrementedEpoch<E>,
639 ) -> Result<(), pez_fork_tree::Error<D::Error>> {
640 let is_descendent_of =
641 descendent_of_builder.build_is_descendent_of(Some((hash, parent_hash)));
642 let IncrementedEpoch(epoch) = epoch;
643 let header = PersistedEpochHeader::<E>::from(&epoch);
644
645 let res = self.inner.import(hash, number, header, &is_descendent_of);
646
647 match res {
648 Ok(_) | Err(pez_fork_tree::Error::Duplicate) => {
649 self.epochs.insert((hash, number), epoch);
650 Ok(())
651 },
652 Err(e) => Err(e),
653 }
654 }
655
656 pub fn reset(&mut self, parent_hash: Hash, hash: Hash, number: Number, current: E, next: E) {
659 self.inner = ForkTree::new();
660 self.epochs.clear();
661 let persisted = PersistedEpoch::Regular(current);
662 let header = PersistedEpochHeader::from(&persisted);
663 let _res = self.inner.import(parent_hash, number - One::one(), header, &|_, _| {
664 Ok(false) as Result<bool, pez_fork_tree::Error<ClientError>>
665 });
666 self.epochs.insert((parent_hash, number - One::one()), persisted);
667
668 let persisted = PersistedEpoch::Regular(next);
669 let header = PersistedEpochHeader::from(&persisted);
670 let _res = self.inner.import(hash, number, header, &|_, _| {
671 Ok(true) as Result<bool, pez_fork_tree::Error<ClientError>>
672 });
673 self.epochs.insert((hash, number), persisted);
674 }
675
676 pub fn revert<D: IsDescendentOfBuilder<Hash>>(
680 &mut self,
681 descendent_of_builder: D,
682 hash: Hash,
683 number: Number,
684 ) {
685 let is_descendent_of = descendent_of_builder.build_is_descendent_of(None);
686
687 let filter = |node_hash: &Hash, node_num: &Number, _: &PersistedEpochHeader<E>| {
688 if number >= *node_num
689 && (is_descendent_of(node_hash, &hash).unwrap_or_default() || *node_hash == hash)
690 {
691 FilterAction::KeepNode
693 } else if number < *node_num && is_descendent_of(&hash, node_hash).unwrap_or_default() {
694 FilterAction::Remove
696 } else {
697 FilterAction::KeepTree
699 }
700 };
701
702 self.inner.drain_filter(filter).for_each(|(h, n, _)| {
703 self.epochs.remove(&(h, n));
704 });
705 }
706
707 pub fn tree(&self) -> &ForkTree<Hash, Number, PersistedEpochHeader<E>> {
709 &self.inner
710 }
711}
712
713pub type EpochChangesFor<Block, Epoch> =
715 EpochChanges<<Block as BlockT>::Hash, NumberFor<Block>, Epoch>;
716
717pub type SharedEpochChanges<Block, Epoch> =
719 pezsc_consensus::shared_data::SharedData<EpochChangesFor<Block, Epoch>>;
720
721#[cfg(test)]
722mod tests {
723 use super::{Epoch as EpochT, *};
724
725 #[derive(Debug, PartialEq)]
726 pub struct TestError;
727
728 impl std::fmt::Display for TestError {
729 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
730 write!(f, "TestError")
731 }
732 }
733
734 impl std::error::Error for TestError {}
735
736 impl<'a, F: 'a, H: 'a + PartialEq + std::fmt::Debug> IsDescendentOfBuilder<H> for &'a F
737 where
738 F: Fn(&H, &H) -> Result<bool, TestError>,
739 {
740 type Error = TestError;
741 type IsDescendentOf = Box<dyn Fn(&H, &H) -> Result<bool, TestError> + 'a>;
742
743 fn build_is_descendent_of(&self, current: Option<(H, H)>) -> Self::IsDescendentOf {
744 let f = *self;
745 Box::new(move |base, head| {
746 let mut head = head;
747
748 if let Some((ref c_head, ref c_parent)) = current {
749 if head == c_head {
750 if base == c_parent {
751 return Ok(true);
752 } else {
753 head = c_parent;
754 }
755 }
756 }
757
758 f(base, head)
759 })
760 }
761 }
762
763 type Hash = [u8; 1];
764 type Slot = u64;
765
766 #[derive(Debug, Clone, Eq, PartialEq)]
767 struct Epoch {
768 start_slot: Slot,
769 duration: Slot,
770 }
771
772 impl EpochT for Epoch {
773 type NextEpochDescriptor = ();
774 type Slot = Slot;
775
776 fn increment(&self, _: ()) -> Self {
777 Epoch { start_slot: self.start_slot + self.duration, duration: self.duration }
778 }
779
780 fn end_slot(&self) -> Slot {
781 self.start_slot + self.duration
782 }
783
784 fn start_slot(&self) -> Slot {
785 self.start_slot
786 }
787 }
788
789 #[test]
790 fn genesis_epoch_is_created_but_not_imported() {
791 let is_descendent_of = |base: &Hash, block: &Hash| -> Result<bool, TestError> {
797 match (base, *block) {
798 (b"A", b) => Ok(b == *b"B" || b == *b"C" || b == *b"D"),
799 (b"B", b) | (b"C", b) => Ok(b == *b"D"),
800 (b"0", _) => Ok(true),
801 _ => Ok(false),
802 }
803 };
804
805 let epoch_changes = EpochChanges::<_, _, Epoch>::new();
806 let genesis_epoch = epoch_changes
807 .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 10101)
808 .unwrap()
809 .unwrap();
810
811 match genesis_epoch {
812 ViableEpochDescriptor::UnimportedGenesis(slot) => {
813 assert_eq!(slot, 10101u64);
814 },
815 _ => panic!("should be unimported genesis"),
816 };
817
818 let genesis_epoch_2 = epoch_changes
819 .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 10102)
820 .unwrap()
821 .unwrap();
822
823 match genesis_epoch_2 {
824 ViableEpochDescriptor::UnimportedGenesis(slot) => {
825 assert_eq!(slot, 10102u64);
826 },
827 _ => panic!("should be unimported genesis"),
828 };
829 }
830
831 #[test]
832 fn epoch_changes_between_blocks() {
833 let is_descendent_of = |base: &Hash, block: &Hash| -> Result<bool, TestError> {
839 match (base, *block) {
840 (b"A", b) => Ok(b == *b"B" || b == *b"C" || b == *b"D"),
841 (b"B", b) | (b"C", b) => Ok(b == *b"D"),
842 (b"0", _) => Ok(true),
843 _ => Ok(false),
844 }
845 };
846
847 let make_genesis = |slot| Epoch { start_slot: slot, duration: 100 };
848
849 let mut epoch_changes = EpochChanges::<_, _, Epoch>::new();
850 let genesis_epoch = epoch_changes
851 .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 100)
852 .unwrap()
853 .unwrap();
854
855 assert_eq!(genesis_epoch, ViableEpochDescriptor::UnimportedGenesis(100));
856
857 let import_epoch_1 =
858 epoch_changes.viable_epoch(&genesis_epoch, &make_genesis).unwrap().increment(());
859 let epoch_1 = import_epoch_1.as_ref().clone();
860
861 epoch_changes
862 .import(&is_descendent_of, *b"A", 1, *b"0", import_epoch_1)
863 .unwrap();
864 let genesis_epoch = epoch_changes.epoch_data(&genesis_epoch, &make_genesis).unwrap();
865
866 assert!(is_descendent_of(b"0", b"A").unwrap());
867
868 let end_slot = genesis_epoch.end_slot();
869 assert_eq!(end_slot, epoch_1.start_slot);
870
871 {
872 let x = epoch_changes
874 .epoch_data_for_child_of(&is_descendent_of, b"A", 1, end_slot - 1, &make_genesis)
875 .unwrap()
876 .unwrap();
877
878 assert_eq!(x, genesis_epoch);
879 }
880
881 {
882 let x = epoch_changes
885 .epoch_data_for_child_of(&is_descendent_of, b"A", 1, end_slot, &make_genesis)
886 .unwrap()
887 .unwrap();
888
889 assert_eq!(x, epoch_1);
890 }
891
892 {
893 let x = epoch_changes
896 .epoch_data_for_child_of(
897 &is_descendent_of,
898 b"A",
899 1,
900 epoch_1.end_slot() - 1,
901 &make_genesis,
902 )
903 .unwrap()
904 .unwrap();
905
906 assert_eq!(x, epoch_1);
907 }
908 }
909
910 #[test]
911 fn two_block_ones_dont_conflict() {
912 let is_descendent_of = |base: &Hash, block: &Hash| -> Result<bool, TestError> {
917 match (base, *block) {
918 (b"A", b) => Ok(b == *b"B"),
919 (b"X", b) => Ok(b == *b"Y"),
920 (b"0", _) => Ok(true),
921 _ => Ok(false),
922 }
923 };
924
925 let duration = 100;
926
927 let make_genesis = |slot| Epoch { start_slot: slot, duration };
928
929 let mut epoch_changes = EpochChanges::new();
930 let next_descriptor = ();
931
932 {
934 let genesis_epoch_a_descriptor = epoch_changes
935 .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 100)
936 .unwrap()
937 .unwrap();
938
939 let incremented_epoch = epoch_changes
940 .viable_epoch(&genesis_epoch_a_descriptor, &make_genesis)
941 .unwrap()
942 .increment(next_descriptor);
943
944 epoch_changes
945 .import(&is_descendent_of, *b"A", 1, *b"0", incremented_epoch)
946 .unwrap();
947 }
948
949 {
951 let genesis_epoch_x_descriptor = epoch_changes
952 .epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 1000)
953 .unwrap()
954 .unwrap();
955
956 let incremented_epoch = epoch_changes
957 .viable_epoch(&genesis_epoch_x_descriptor, &make_genesis)
958 .unwrap()
959 .increment(next_descriptor);
960
961 epoch_changes
962 .import(&is_descendent_of, *b"X", 1, *b"0", incremented_epoch)
963 .unwrap();
964 }
965
966 {
969 let epoch_for_a_child = epoch_changes
970 .epoch_data_for_child_of(&is_descendent_of, b"A", 1, 101, &make_genesis)
971 .unwrap()
972 .unwrap();
973
974 assert_eq!(epoch_for_a_child, make_genesis(100));
975
976 let epoch_for_x_child = epoch_changes
977 .epoch_data_for_child_of(&is_descendent_of, b"X", 1, 1001, &make_genesis)
978 .unwrap()
979 .unwrap();
980
981 assert_eq!(epoch_for_x_child, make_genesis(1000));
982
983 let epoch_for_x_child_before_genesis = epoch_changes
984 .epoch_data_for_child_of(&is_descendent_of, b"X", 1, 101, &make_genesis)
985 .unwrap();
986
987 assert!(epoch_for_x_child_before_genesis.is_none());
990 }
991 }
992
993 #[test]
994 fn prune_removes_stale_nodes() {
995 let is_descendent_of = |base: &Hash, block: &Hash| -> Result<bool, TestError> {
1009 match (base, block) {
1010 (b"0", _) => Ok(true),
1011 (b"A", b) => Ok(b != b"0"),
1012 (b"B", b) => Ok(b != b"0" && b != b"A" && b != b"D"),
1013 (b"C", b) => Ok(b == b"F" || b == b"G" || b == b"y"),
1014 (b"x", b) => Ok(b == b"C" || b == b"F" || b == b"G" || b == b"y"),
1015 (b"y", b) => Ok(b == b"G"),
1016 _ => Ok(false),
1017 }
1018 };
1019
1020 let mut epoch_changes = EpochChanges::new();
1021
1022 let mut import_at = |slot, hash: &Hash, number, parent_hash, parent_number| {
1023 let make_genesis = |slot| Epoch { start_slot: slot, duration: 100 };
1024 let epoch_descriptor = epoch_changes
1026 .epoch_descriptor_for_child_of(&is_descendent_of, parent_hash, parent_number, slot)
1027 .unwrap()
1028 .unwrap();
1029 let next_epoch_desc = epoch_changes
1031 .viable_epoch(&epoch_descriptor, &make_genesis)
1032 .unwrap()
1033 .increment(());
1034 epoch_changes
1036 .import(&is_descendent_of, *hash, number, *parent_hash, next_epoch_desc)
1037 .unwrap();
1038 };
1039
1040 import_at(100, b"A", 10, b"0", 0);
1041 import_at(200, b"B", 20, b"A", 10);
1042 import_at(300, b"C", 30, b"B", 20);
1043 import_at(200, b"D", 20, b"A", 10);
1044 import_at(300, b"E", 30, b"B", 20);
1045 import_at(400, b"F", 40, b"C", 30);
1046 import_at(400, b"G", 40, b"C", 30);
1047 import_at(100, b"H", 10, b"0", 0);
1048
1049 let mut nodes: Vec<_> = epoch_changes.tree().iter().map(|(h, _, _)| h).collect();
1050 nodes.sort();
1051 assert_eq!(nodes, vec![b"A", b"B", b"C", b"D", b"E", b"F", b"G", b"H"]);
1052
1053 epoch_changes.prune_finalized(&is_descendent_of, b"x", 25, 230).unwrap();
1059
1060 let mut nodes: Vec<_> = epoch_changes.tree().iter().map(|(h, _, _)| h).collect();
1061 nodes.sort();
1062 assert_eq!(nodes, vec![b"A", b"B", b"C", b"F", b"G"]);
1063
1064 epoch_changes.prune_finalized(&is_descendent_of, b"y", 35, 330).unwrap();
1070
1071 let mut nodes: Vec<_> = epoch_changes.tree().iter().map(|(h, _, _)| h).collect();
1072 nodes.sort();
1073 assert_eq!(nodes, vec![b"B", b"C", b"G"]);
1074 }
1075
1076 #[test]
1077 fn near_genesis_prune_works() {
1078 let is_descendent_of = |base: &Hash, block: &Hash| -> Result<bool, TestError> {
1085 match (block, base) {
1086 | (b"A", b"0")
1087 | (b"B", b"0" | b"A")
1088 | (b"C", b"0" | b"A" | b"B")
1089 | (b"D", b"0" | b"A" | b"B" | b"C")
1090 | (b"E", b"0" | b"A" | b"B" | b"C" | b"D")
1091 | (b"F", b"0" | b"A" | b"B" | b"C" | b"D" | b"E")
1092 | (b"G", b"0" | b"A" | b"B" | b"C" | b"D" | b"E")
1093 | (b"H", b"0" | b"A" | b"B" | b"C" | b"D" | b"E" | b"G")
1094 | (b"I", b"0" | b"A" | b"B" | b"C" | b"D" | b"E" | b"G" | b"H")
1095 | (b"J", b"0" | b"A" | b"B" | b"C" | b"D" | b"E" | b"G" | b"H" | b"I")
1096 | (b"K", b"0" | b"A" | b"B" | b"C" | b"D" | b"E" | b"G" | b"H" | b"I" | b"J")
1097 | (
1098 b"L",
1099 b"0" | b"A" | b"B" | b"C" | b"D" | b"E" | b"G" | b"H" | b"I" | b"J" | b"K",
1100 ) => Ok(true),
1101 _ => Ok(false),
1102 }
1103 };
1104
1105 let mut epoch_changes = EpochChanges::new();
1106
1107 let epoch = Epoch { start_slot: 278183811, duration: 5 };
1108 let epoch = PersistedEpoch::Genesis(epoch.clone(), epoch.increment(()));
1109
1110 epoch_changes
1111 .import(&is_descendent_of, *b"A", 1, Default::default(), IncrementedEpoch(epoch))
1112 .unwrap();
1113
1114 let import_at = |epoch_changes: &mut EpochChanges<_, _, Epoch>,
1115 slot,
1116 hash: &Hash,
1117 number,
1118 parent_hash,
1119 parent_number| {
1120 let make_genesis = |slot| Epoch { start_slot: slot, duration: 5 };
1121 let epoch_descriptor = epoch_changes
1123 .epoch_descriptor_for_child_of(&is_descendent_of, parent_hash, parent_number, slot)
1124 .unwrap()
1125 .unwrap();
1126 let next_epoch_desc = epoch_changes
1128 .viable_epoch(&epoch_descriptor, &make_genesis)
1129 .unwrap()
1130 .increment(());
1131 epoch_changes
1133 .import(&is_descendent_of, *hash, number, *parent_hash, next_epoch_desc)
1134 .unwrap();
1135 };
1136
1137 epoch_changes.prune_finalized(&is_descendent_of, b"C", 3, 278183813).unwrap();
1139
1140 import_at(&mut epoch_changes, 278183816, b"G", 6, b"E", 5);
1141 import_at(&mut epoch_changes, 278183816, b"F", 6, b"E", 5);
1142
1143 epoch_changes.prune_finalized(&is_descendent_of, b"C", 3, 278183813).unwrap();
1145 let mut list: Vec<_> = epoch_changes.inner.iter().map(|e| e.0).collect();
1146 list.sort();
1147 assert_eq!(list, vec![b"A", b"F", b"G"]);
1148
1149 import_at(&mut epoch_changes, 278183821, b"L", 11, b"K", 10);
1150
1151 epoch_changes.prune_finalized(&is_descendent_of, b"J", 9, 278183819).unwrap();
1153 let mut list: Vec<_> = epoch_changes.inner.iter().map(|e| e.0).collect();
1154 list.sort();
1155 assert_eq!(list, vec![b"A", b"G", b"L"]);
1156 }
1157}