krabka_verified/audit/
spool_append_decision.rs1use {
2 super::{AuditCheckpointAdmission, AuditLossMarkerAdmission, AuditLosses, SpoolAppendDecision},
3 creusot_std::prelude::*,
4};
5
6#[ensures((result == AuditCheckpointAdmission::Admit) == (
8 signature_valid && head_matches && expected_seq@ > 0
9 && checkpoint_seq_high@ + 1 == expected_seq@))]
10#[ensures((result == AuditCheckpointAdmission::RejectSignature) == !signature_valid)]
11#[ensures((result == AuditCheckpointAdmission::RejectHead)
12 == (signature_valid && !head_matches))]
13#[ensures((result == AuditCheckpointAdmission::RejectSequence) == (
14 signature_valid && head_matches
15 && (expected_seq@ == 0 || checkpoint_seq_high@ + 1 != expected_seq@)))]
16#[must_use]
17pub fn audit_checkpoint_admission(
18 signature_valid: bool,
19 head_matches: bool,
20 expected_seq: u64,
21 checkpoint_seq_high: u64,
22) -> AuditCheckpointAdmission {
23 if !signature_valid {
24 AuditCheckpointAdmission::RejectSignature
25 } else if !head_matches {
26 AuditCheckpointAdmission::RejectHead
27 } else if expected_seq == 0 || checkpoint_seq_high != expected_seq - 1 {
28 AuditCheckpointAdmission::RejectSequence
29 } else {
30 AuditCheckpointAdmission::Admit
31 }
32}
33
34#[ensures(match result {
38 AuditLossMarkerAdmission::Admit { generation: admitted } => header_matches
39 && field_count@ == 2
40 && count@ > 0
41 && generation == Some(admitted)
42 && admitted@ > previous_generation@,
43 AuditLossMarkerAdmission::Reject => !(header_matches && field_count@ == 2 && count@ > 0
44 && match generation {
45 Some(generation) => generation@ > previous_generation@,
46 None => false,
47 }),
48})]
49#[must_use]
50pub fn audit_loss_marker_admission(
51 header_matches: bool,
52 field_count: u64,
53 count: u64,
54 generation: Option<u64>,
55 previous_generation: u64,
56) -> AuditLossMarkerAdmission {
57 match generation {
58 Some(generation)
59 if header_matches
60 && field_count == 2
61 && count > 0
62 && generation > previous_generation =>
63 {
64 AuditLossMarkerAdmission::Admit { generation }
65 }
66 _ => AuditLossMarkerAdmission::Reject,
67 }
68}
69
70#[ensures(state.generation != batch.generation
87 ==> result.generation == state.generation && result.count == state.count)]
88#[ensures(state.generation == batch.generation && batch.count@ <= state.count@
89 ==> result.count@ + batch.count@ == state.count@)]
90#[ensures(state.generation == batch.generation && batch.count@ > state.count@
91 ==> result.count@ == 0)]
92#[ensures(state.generation == batch.generation
93 ==> (result.generation != state.generation)
94 == (result.count@ > 0 && state.generation@ < u64::MAX@))]
95#[ensures(result.generation != state.generation
96 ==> result.generation@ == state.generation@ + 1)]
97#[must_use]
98pub fn settle_loss_batch(state: AuditLosses, batch: AuditLosses) -> AuditLosses {
99 if state.generation != batch.generation {
100 return state;
101 }
102 if batch.count >= state.count {
103 return AuditLosses {
104 generation: state.generation,
105 count: 0,
106 };
107 }
108 AuditLosses {
109 generation: if state.generation < u64::MAX {
110 state.generation + 1
111 } else {
112 state.generation
113 },
114 count: state.count - batch.count,
115 }
116}
117
118#[requires(sync_every@ > 0)]
120#[requires(unsynced@ < sync_every@)]
121#[ensures(result.accepted
122 == (current_bytes@ <= max_bytes@ && frame_bytes@ <= max_bytes@ - current_bytes@))]
123#[ensures(result.accepted ==> result.new_bytes@ == current_bytes@ + frame_bytes@)]
124#[ensures(result.accepted ==> result.new_bytes@ <= max_bytes@)]
125#[ensures(!result.accepted ==> result.new_bytes@ == current_bytes@)]
126#[ensures(result.sync
127 == (result.accepted && unsynced@ + 1 >= sync_every@))]
128#[ensures(result.next_unsynced@ < sync_every@)]
129#[ensures(result.next_unsynced@ == if result.accepted {
130 if unsynced@ + 1 >= sync_every@ { 0 } else { unsynced@ + 1 }
131} else {
132 unsynced@
133})]
134#[must_use]
135pub fn spool_append_decision(
136 current_bytes: u64,
137 frame_bytes: u64,
138 max_bytes: u64,
139 unsynced: u64,
140 sync_every: u64,
141) -> SpoolAppendDecision {
142 if current_bytes > max_bytes || frame_bytes > max_bytes - current_bytes {
143 return SpoolAppendDecision {
144 accepted: false,
145 new_bytes: current_bytes,
146 sync: false,
147 next_unsynced: unsynced,
148 };
149 }
150
151 let next_unsynced = unsynced + 1;
152 let sync = next_unsynced >= sync_every;
153 SpoolAppendDecision {
154 accepted: true,
155 new_bytes: current_bytes + frame_bytes,
156 sync,
157 next_unsynced: if sync { 0 } else { next_unsynced },
158 }
159}