krabka_verified/transaction/
marker.rs1#[cfg(creusot)]
2use super::{TransactionIdentity, TransactionSnapshot};
3use {
4 super::{
5 LogBatchKind, TransactionMarkerMaterializationDecision, TransactionMarkerPartitionState,
6 TransactionMarkerRequest,
7 },
8 creusot_std::prelude::*,
9};
10
11#[ensures(result == (transaction_version@ >= 2
14 && producer_epoch == current_producer_epoch
15 && has_pending_transaction && producer_epoch@ != i16::MAX@))]
16#[must_use]
17pub fn transaction_marker_equal_epoch_fenced(
18 transaction_version: i16,
19 producer_epoch: i16,
20 current_producer_epoch: i16,
21 has_pending_transaction: bool,
22) -> bool {
23 transaction_version >= 2
24 && producer_epoch == current_producer_epoch
25 && has_pending_transaction
26 && producer_epoch != i16::MAX
27}
28
29#[ensures((result == LogBatchKind::Data) == !is_control)]
30#[ensures((result == LogBatchKind::Abort) ==
31 (is_control && [email protected]() >= 4 && key@[2]@ == 0 && key@[3]@ == 0))]
32#[ensures((result == LogBatchKind::Commit) ==
33 (is_control && [email protected]() >= 4 && key@[2]@ == 0 && key@[3]@ == 1))]
34#[ensures((result == LogBatchKind::Barrier) ==
35 (is_control && [email protected]() >= 4 && key@[2]@ == 3 && key@[3]@ == 232))]
36#[ensures((result == LogBatchKind::OtherControl) == (is_control
37 && !([email protected]() >= 4 && ((key@[2]@ == 0 && (key@[3]@ == 0 || key@[3]@ == 1))
38 || (key@[2]@ == 3 && key@[3]@ == 232)))))]
39#[must_use]
40pub fn log_batch_kind(is_control: bool, key: &[u8]) -> LogBatchKind {
41 if !is_control {
42 return LogBatchKind::Data;
43 }
44 if key.len() < 4 {
45 return LogBatchKind::OtherControl;
46 }
47 match (key.get(2), key.get(3)) {
48 (Some(0), Some(0)) => LogBatchKind::Abort,
49 (Some(0), Some(1)) => LogBatchKind::Commit,
50 (Some(3), Some(232)) => LogBatchKind::Barrier,
51 _ => LogBatchKind::OtherControl,
52 }
53}
54
55#[cfg(creusot)]
57#[cfg_attr(test, mutants::skip)]
58#[logic(open)]
59pub fn marker_well_formed(
62 request: TransactionMarkerRequest,
63 current: TransactionMarkerPartitionState,
64) -> bool {
65 pearlite! {
66 request.producer_id@ >= 0
67 && request.producer_epoch@ >= 0
68 && request.coordinator_epoch@ >= -1
69 && current.producer_epoch@ >= -1
70 && current.coordinator_epoch@ >= -1
71 && (!current.has_pending_transaction || current.producer_epoch@ >= 0)
72 }
73}
74
75#[cfg(creusot)]
77#[cfg_attr(test, mutants::skip)]
78#[logic(open)]
79pub fn marker_generation_current(
83 request: TransactionMarkerRequest,
84 current: TransactionMarkerPartitionState,
85) -> bool {
86 pearlite! {
87 request.producer_epoch@ >= current.producer_epoch@
88 && request.coordinator_epoch@ >= current.coordinator_epoch@
89 }
90}
91
92#[cfg(creusot)]
94#[cfg_attr(test, mutants::skip)]
95#[logic(open)]
96pub fn marker_completed_retry(
99 request: TransactionMarkerRequest,
100 current: TransactionMarkerPartitionState,
101) -> bool {
102 pearlite! {
103 !current.has_pending_transaction
104 && request.producer_epoch@ == current.producer_epoch@
105 && request.coordinator_epoch@ == current.coordinator_epoch@
106 }
107}
108
109#[cfg(creusot)]
111#[cfg_attr(test, mutants::skip)]
112#[logic(open)]
113pub fn marker_publishes_offsets(
116 request: TransactionMarkerRequest,
117 current: TransactionMarkerPartitionState,
118) -> bool {
119 pearlite! {
120 current.has_pending_transaction && request.is_commit && request.is_offsets_partition
121 }
122}
123
124#[ensures((result == TransactionMarkerMaterializationDecision::RejectMalformed)
128 == !marker_well_formed(request, current))]
129#[ensures((result == TransactionMarkerMaterializationDecision::RejectProducerEpoch)
130 == (marker_well_formed(request, current)
131 && request.producer_epoch@ < current.producer_epoch@))]
132#[ensures((result == TransactionMarkerMaterializationDecision::RejectCoordinatorEpoch)
133 == (marker_well_formed(request, current)
134 && request.producer_epoch@ >= current.producer_epoch@
135 && request.coordinator_epoch@ < current.coordinator_epoch@))]
136#[ensures((result == TransactionMarkerMaterializationDecision::Retry)
137 == (marker_well_formed(request, current) && marker_completed_retry(request, current)))]
138#[ensures((result == TransactionMarkerMaterializationDecision::AppendAndPublishOffsets)
139 == (marker_well_formed(request, current)
140 && marker_generation_current(request, current)
141 && marker_publishes_offsets(request, current)))]
142#[ensures((result == TransactionMarkerMaterializationDecision::AppendWithoutOffsetPublication)
143 == (marker_well_formed(request, current)
144 && marker_generation_current(request, current)
145 && !marker_completed_retry(request, current)
146 && !marker_publishes_offsets(request, current)))]
147#[must_use]
148pub fn transaction_marker_materialization_decision(
149 request: TransactionMarkerRequest,
150 current: TransactionMarkerPartitionState,
151) -> TransactionMarkerMaterializationDecision {
152 if request.producer_id < 0
153 || request.producer_epoch < 0
154 || request.coordinator_epoch < -1
155 || current.producer_epoch < -1
156 || current.coordinator_epoch < -1
157 || (current.has_pending_transaction && current.producer_epoch == -1)
158 {
159 TransactionMarkerMaterializationDecision::RejectMalformed
160 } else if request.producer_epoch < current.producer_epoch {
161 TransactionMarkerMaterializationDecision::RejectProducerEpoch
162 } else if request.coordinator_epoch < current.coordinator_epoch {
163 TransactionMarkerMaterializationDecision::RejectCoordinatorEpoch
164 } else if !current.has_pending_transaction
165 && request.producer_epoch == current.producer_epoch
166 && request.coordinator_epoch == current.coordinator_epoch
167 {
168 TransactionMarkerMaterializationDecision::Retry
169 } else if current.has_pending_transaction && request.is_commit && request.is_offsets_partition {
170 TransactionMarkerMaterializationDecision::AppendAndPublishOffsets
171 } else {
172 TransactionMarkerMaterializationDecision::AppendWithoutOffsetPublication
173 }
174}
175
176#[cfg(creusot)]
178#[cfg_attr(test, mutants::skip)]
179#[logic(open)]
180pub fn is_identity(snapshot: TransactionSnapshot) -> bool {
182 pearlite! { snapshot.pid@ >= 0 && snapshot.epoch@ >= 0 }
183}
184
185#[cfg(creusot)]
187#[cfg_attr(test, mutants::skip)]
188#[logic(open)]
189pub fn has_identity(snapshot: TransactionSnapshot, identity: TransactionIdentity) -> bool {
191 pearlite! { snapshot.pid == identity.pid && snapshot.epoch == identity.epoch }
192}
193
194#[cfg(creusot)]
196#[cfg_attr(test, mutants::skip)]
197#[logic(open)]
198pub fn snapshot_eq(left: TransactionSnapshot, right: TransactionSnapshot) -> bool {
200 pearlite! {
201 left.pid == right.pid && left.epoch == right.epoch && left.state == right.state
202 }
203}