krabka_verified/
checkpoint.rs1use creusot_std::prelude::ensures;
4#[cfg(creusot)]
5use creusot_std::prelude::{Int, invariant};
6
7#[ensures(result == (candidate_end@ > current_end@
9 || (candidate_end@ == current_end@ && candidate_epoch@ > current_epoch@)))]
10#[must_use]
11pub fn checkpoint_id_newer(
12 candidate_end: i64,
13 candidate_epoch: i32,
14 current_end: i64,
15 current_epoch: i32,
16) -> bool {
17 candidate_end > current_end || (candidate_end == current_end && candidate_epoch > current_epoch)
18}
19
20#[ensures(match result {
22 Some(selected) => selected@ < [email protected]()
23 && (forall<i: Int> 0 <= i && i < [email protected]()
24 ==> ids@[selected@].0@ > ids@[i].0@
25 || (ids@[selected@].0@ == ids@[i].0@
26 && ids@[selected@].1@ >= ids@[i].1@)),
27 None => [email protected]() == 0,
28})]
29#[must_use]
30pub fn latest_checkpoint_index(ids: &[(i64, i32)]) -> Option<usize> {
31 if matches!(ids.len(), 0) {
32 return None;
33 }
34 let mut selected = 0usize;
35 let mut i = 1usize;
36 #[invariant(1 <= i@ && i@ <= [email protected]())]
37 #[invariant(selected@ < i@)]
38 #[invariant(forall<k: Int> 0 <= k && k < i@
39 ==> ids@[selected@].0@ > ids@[k].0@
40 || (ids@[selected@].0@ == ids@[k].0@
41 && ids@[selected@].1@ >= ids@[k].1@))]
42 #[variant([email protected]() - i@)]
43 while i < ids.len() {
44 if let (Some(&(offset, epoch)), Some(&(selected_offset, selected_epoch))) =
45 (ids.get(i), ids.get(selected))
46 && checkpoint_id_newer(offset, epoch, selected_offset, selected_epoch)
47 {
48 selected = i;
49 }
50 i += 1;
51 }
52 Some(selected)
53}
54
55#[ensures(result == ((candidate_end@ == selected_end@
70 && candidate_epoch@ == selected_epoch@)
71 || (candidate_end@ == previous_end@ && candidate_epoch@ == previous_epoch@)))]
72#[must_use]
73pub fn checkpoint_id_retained(
74 candidate_end: i64,
75 candidate_epoch: i32,
76 selected_end: i64,
77 selected_epoch: i32,
78 previous_end: i64,
79 previous_epoch: i32,
80) -> bool {
81 (candidate_end == selected_end && candidate_epoch == selected_epoch)
82 || (candidate_end == previous_end && candidate_epoch == previous_epoch)
83}
84
85#[cfg(test)]
86mod tests {
87 use super::{checkpoint_id_retained, latest_checkpoint_index};
88
89 #[test]
90 fn latest_index_covers_empty_ties_and_lexicographic_order() {
91 assert2::check!(latest_checkpoint_index(&[]) == None);
92 assert2::check!(latest_checkpoint_index(&[(10, 2)]) == Some(0));
93 assert2::check!(latest_checkpoint_index(&[(10, 2), (10, 9), (11, 1)]) == Some(2));
94 assert2::check!(latest_checkpoint_index(&[(10, 5), (10, 2)]) == Some(0));
95 assert2::check!(latest_checkpoint_index(&[(11, 1), (11, 1), (10, 99)]) == Some(0));
96 }
97
98 #[test]
99 fn retention_keeps_exactly_the_two_given_full_ids() {
100 assert2::check!(checkpoint_id_retained(11, 3, 11, 3, 10, 3));
103 assert2::check!(checkpoint_id_retained(10, 3, 11, 3, 10, 3));
104 assert2::check!(!checkpoint_id_retained(9, 3, 11, 3, 10, 3));
105 assert2::check!(!checkpoint_id_retained(11, 2, 11, 3, 10, 3));
106 assert2::check!(!checkpoint_id_retained(10, 2, 11, 3, 10, 3));
107
108 assert2::check!(checkpoint_id_retained(11, 3, 11, 3, 11, 3));
111 assert2::check!(!checkpoint_id_retained(10, 3, 11, 3, 11, 3));
112 }
113}