krabka_verified/retention/
remote_retention_model.rs1#[cfg(creusot)]
2use super::{RemoteRetentionSegment, local_prefix_bytes};
3use {super::LocalRetentionSegment, creusot_std::prelude::*};
4
5#[ensures(result == (retained_cuts@ > 0
11 && published_epoch@ - retained_cuts@ >= i64::MIN@
12 && held_epoch@ <= published_epoch@ - retained_cuts@))]
13#[must_use]
14pub fn barrier_cut_expired(published_epoch: i64, retained_cuts: i32, held_epoch: i64) -> bool {
15 if retained_cuts <= 0 {
16 return false;
17 }
18 let retained_cuts = i64::from(retained_cuts);
19 if published_epoch < i64::MIN + retained_cuts {
20 return false;
21 }
22 held_epoch <= published_epoch - retained_cuts
23}
24
25#[cfg(creusot)]
27#[cfg_attr(test, mutants::skip)]
28#[logic(open)]
29pub fn local_retention_limit(segments: Seq<LocalRetentionSegment>) -> Int {
35 pearlite! {
36 if segments.len() > 0 && segments[segments.len() - 1].size@ == 0 {
37 segments.len() - 1
38 } else {
39 segments.len()
40 }
41 }
42}
43
44#[cfg(creusot)]
46#[cfg_attr(test, mutants::skip)]
47#[logic(open)]
48#[variant(limit - i)]
70pub fn local_retention_model(
71 segments: Seq<LocalRetentionSegment>,
72 limit: Int,
73 i: Int,
74 debt: Int,
75 sizing: bool,
76) -> Int {
77 pearlite! {
78 if i >= limit || i >= segments.len() || segments[i].blocked {
79 i
80 } else if sizing && segments[i].size@ <= debt {
81 local_retention_model(segments, limit, i + 1, debt - segments[i].size@, true)
82 } else if segments[i].expired {
83 local_retention_model(segments, limit, i + 1, debt, false)
84 } else {
85 i
86 }
87 }
88}
89
90#[ensures(result@ == match size_debt {
114 None => local_retention_model(
115 segments@, local_retention_limit(segments@), 0, 0, false),
116 Some(debt) => local_retention_model(
117 segments@, local_retention_limit(segments@), 0, debt@, true),
118})]
119#[ensures(result@ <= [email protected]())]
120#[ensures(forall<i: Int> 0 <= i && i < result@ ==> !segments@[i].blocked)]
121#[ensures([email protected]() > 0 && segments@[[email protected]() - 1].size@ == 0
122 ==> result@ < [email protected]())]
123#[ensures(result@ < local_retention_limit(segments@)
124 ==> segments@[result@].blocked || !segments@[result@].expired)]
125#[ensures(forall<i: Int> 0 <= i && i < result@ ==> segments@[i].expired
126 || match size_debt { None => false, Some(debt) => local_prefix_bytes(segments@, i + 1) <= debt@ })]
127#[ensures(result@ < local_retention_limit(segments@) && !segments@[result@].blocked
128 ==> match size_debt { None => true, Some(debt) => local_prefix_bytes(segments@, result@ + 1) > debt@ })]
129#[must_use]
130pub fn local_retention_prefix(segments: &[LocalRetentionSegment], size_debt: Option<u64>) -> usize {
131 let limit = match segments.len().checked_sub(1) {
132 Some(newest) if matches!(segments.get(newest), Some(segment) if segment.size == 0) => {
133 newest
134 }
135 _ => segments.len(),
136 };
137 let (mut debt, mut sizing) = (0, false);
138 if let Some(initial) = size_debt {
139 (debt, sizing) = (initial, true);
140 }
141 let mut len = 0usize;
142 #[invariant(len@ <= limit@)]
143 #[invariant(forall<i: Int> 0 <= i && i < len@ ==> !segments@[i].blocked)]
144 #[invariant(local_prefix_bytes(segments@, len@) >= 0)]
145 #[invariant(match size_debt {
146 None => !sizing,
147 Some(initial) => if sizing {
148 local_prefix_bytes(segments@, len@) + debt@ == initial@
149 } else { local_prefix_bytes(segments@, len@) > initial@ },
150 })]
151 #[invariant(forall<i: Int> 0 <= i && i < len@ ==> segments@[i].expired
152 || match size_debt { None => false, Some(initial) => local_prefix_bytes(segments@, i + 1) <= initial@ })]
153 #[invariant(limit@ == local_retention_limit(segments@))]
154 #[invariant(local_retention_model(segments@, limit@, len@, debt@, sizing) == match size_debt {
155 None => local_retention_model(segments@, limit@, 0, 0, false),
156 Some(initial) => local_retention_model(segments@, limit@, 0, initial@, true),
157 })]
158 #[variant(limit@ - len@)]
159 while len < limit {
160 let Some(&segment) = segments.get(len) else {
161 break;
162 };
163 if segment.blocked {
164 break;
165 }
166 if sizing && segment.size <= debt {
167 debt -= segment.size;
168 } else if segment.expired {
169 sizing = false;
170 } else {
171 break;
172 }
173 len += 1;
174 }
175 len
176}
177
178#[cfg(creusot)]
180#[cfg_attr(test, mutants::skip)]
181#[logic(open)]
182#[variant(segments.len() - i)]
193pub fn remote_retention_model(segments: Seq<RemoteRetentionSegment>, i: Int, debt: Int) -> Int {
194 pearlite! {
195 if i >= segments.len() || segments[i].blocked {
196 i
197 } else if segments[i].log_start_breached {
198 remote_retention_model(segments, i + 1, debt)
199 } else if segments[i].time_expired {
200 remote_retention_model(
201 segments,
202 i + 1,
203 if segments[i].size@ <= debt { debt - segments[i].size@ } else { 0 },
204 )
205 } else if debt > 0 && segments[i].size@ <= debt {
206 remote_retention_model(segments, i + 1, debt - segments[i].size@)
207 } else {
208 i
209 }
210 }
211}