krabka_verified/consensus/
watermark.rs1#[cfg(creusot)]
2use super::{count_ge, count_ge_prefix, hwm_member_at, lemma_hwm_member_maximal};
3use creusot_std::prelude::*;
4
5#[cfg(creusot)]
7#[cfg_attr(test, mutants::skip)]
8#[logic(open)]
9fn watermark_inputs_bounded(current: Int, end: Int, followers: Seq<i64>) -> bool {
11 pearlite! {
12 current <= end && (forall<k: Int> 0 <= k && k < followers.len() ==> followers[k]@ <= end)
13 }
14}
15
16#[requires(1 <= majority@ && majority@ <= [email protected]() + 1)]
17#[ensures(result == (count_ge(log_end@, follower_offsets@, cand@, leader_counts) >= majority@))]
18pub(super) fn candidate_has_majority(
19 log_end: i64,
20 follower_offsets: &[i64],
21 cand: i64,
22 majority: usize,
23 leader_counts: bool,
24) -> bool {
25 let mut count: usize = 0;
26 if leader_counts && log_end >= cand && count < majority {
27 count += 1;
28 }
29
30 let n = follower_offsets.len();
31 let mut j = 0;
32 #[invariant(j@ <= n@)]
33 #[invariant({
34 let seen = count_ge_prefix(log_end@, follower_offsets@, cand@, j@ + 1, leader_counts);
35 count@ == if seen < majority@ { seen } else { majority@ }
36 })]
37 #[invariant(count@ <= majority@)]
38 #[variant(n@ - j@)]
39 while j < n {
40 if let Some(&x) = follower_offsets.get(j)
41 && x >= cand
42 && count < majority
43 {
44 count += 1;
45 }
46 j += 1;
47 }
48
49 count >= majority
50}
51
52#[requires(1 <= majority@ && majority@ <= [email protected]() + 1)]
69#[requires(leader_counts || majority@ <= [email protected]())]
70#[requires(watermark_inputs_bounded(current_hwm@, log_end@, follower_offsets@))]
71#[ensures(result@ >= current_hwm@)]
72#[ensures(result@ <= log_end@)]
73#[ensures(forall<v: Int> v > epoch_start_offset@
74 && count_ge(log_end@, follower_offsets@, v, leader_counts) >= majority@
75 ==> v <= result@)]
76#[ensures(result@ > current_hwm@
77 ==> result@ > epoch_start_offset@
78 && count_ge(log_end@, follower_offsets@, result@, leader_counts) >= majority@)]
79#[must_use]
80pub fn recompute_high_watermark(
81 log_end: i64,
82 follower_offsets: &[i64],
83 majority: usize,
84 epoch_start_offset: i64,
85 current_hwm: i64,
86 leader_counts: bool,
87) -> i64 {
88 let n = follower_offsets.len();
89 let mut majority_offset =
90 if candidate_has_majority(log_end, follower_offsets, log_end, majority, leader_counts) {
91 log_end
92 } else {
93 i64::MIN
94 };
95
96 let mut i = 0;
97 #[invariant(i@ <= n@)]
98 #[invariant(majority_offset@ <= log_end@)]
99 #[invariant(majority_offset@ == -9223372036854775807 - 1
100 || count_ge(log_end@, follower_offsets@, majority_offset@, leader_counts) >= majority@)]
101 #[invariant(forall<k: Int> 0 <= k && k < i@ + 1
102 && count_ge(log_end@, follower_offsets@, hwm_member_at(log_end@, follower_offsets@, k), leader_counts) >= majority@
103 ==> hwm_member_at(log_end@, follower_offsets@, k) <= majority_offset@)]
104 #[variant(n@ - i@)]
105 while i < n {
106 if let Some(&cand) = follower_offsets.get(i)
107 && cand > majority_offset
108 && candidate_has_majority(log_end, follower_offsets, cand, majority, leader_counts)
109 {
110 majority_offset = cand;
111 }
112 i += 1;
113 }
114 #[cfg(creusot)]
115 lemma_hwm_member_maximal(
116 log_end,
117 follower_offsets,
118 majority,
119 majority_offset,
120 leader_counts,
121 );
122 let gated = if majority_offset > epoch_start_offset {
123 majority_offset
124 } else {
125 current_hwm
126 };
127 gated.max(current_hwm)
128}
129
130#[requires(1 <= majority@ && majority@ <= [email protected]() + 1)]
142#[requires(watermark_inputs_bounded(current@, log_end@, follower_offsets@))]
143#[ensures(result@ >= current@)]
144#[ensures(result@ <= log_end@)]
145#[ensures(forall<v: Int> count_ge(log_end@, follower_offsets@, v, true) >= majority@
146 ==> v <= result@)]
147#[ensures(result@ > current@
148 ==> count_ge(log_end@, follower_offsets@, result@, true) >= majority@)]
149#[must_use]
150pub fn majority_watermark(
151 log_end: i64,
152 follower_offsets: &[i64],
153 majority: usize,
154 current: i64,
155) -> i64 {
156 recompute_high_watermark(log_end, follower_offsets, majority, current, current, true)
157}