1use creusot_std::prelude::*;
4
5#[krabka_macros::model_type]
7pub enum VoterWireDecision {
8 NegativeId,
9 DirectoryMismatch,
10 InvalidEndpoint,
11 InvalidVersionRange,
12 Accept,
13}
14
15#[ensures((result == VoterWireDecision::NegativeId) == (voter_id@ < 0))]
18#[ensures((result == VoterWireDecision::DirectoryMismatch) == (
19 voter_id@ >= 0 && !directory_id_exact
20))]
21#[ensures((result == VoterWireDecision::InvalidEndpoint) == (
22 voter_id@ >= 0 && directory_id_exact && (endpoint_count@ == 0 || !endpoints_valid)
23))]
24#[ensures((result == VoterWireDecision::InvalidVersionRange) == (
25 voter_id@ >= 0 && directory_id_exact && endpoint_count@ > 0 && endpoints_valid
26 && (min_supported_version@ < 0
27 || max_supported_version@ < 0
28 || min_supported_version@ > max_supported_version@)
29))]
30#[ensures((result == VoterWireDecision::Accept) == (
31 voter_id@ >= 0 && directory_id_exact && endpoint_count@ > 0 && endpoints_valid
32 && min_supported_version@ >= 0
33 && min_supported_version@ <= max_supported_version@
34))]
35#[must_use]
36pub fn voter_wire_decision(
37 voter_id: i32,
38 directory_id_exact: bool,
39 endpoint_count: usize,
40 endpoints_valid: bool,
41 min_supported_version: i16,
42 max_supported_version: i16,
43) -> VoterWireDecision {
44 if voter_id < 0 {
45 VoterWireDecision::NegativeId
46 } else if !directory_id_exact {
47 VoterWireDecision::DirectoryMismatch
48 } else if endpoint_count == 0 || !endpoints_valid {
49 VoterWireDecision::InvalidEndpoint
50 } else if min_supported_version < 0
51 || max_supported_version < 0
52 || min_supported_version > max_supported_version
53 {
54 VoterWireDecision::InvalidVersionRange
55 } else {
56 VoterWireDecision::Accept
57 }
58}
59
60#[krabka_macros::model_type]
62pub enum VoterSetWireDecision {
63 UnsupportedRecordVersion,
64 Empty,
65 DuplicateId,
66 Accept,
67}
68
69#[ensures((result == VoterSetWireDecision::UnsupportedRecordVersion)
72 == (record_version@ != 0))]
73#[ensures((result == VoterSetWireDecision::Empty)
74 == (record_version@ == 0 && voter_count@ == 0))]
75#[ensures((result == VoterSetWireDecision::DuplicateId) == (
76 record_version@ == 0 && voter_count@ > 0 && !voter_ids_unique
77))]
78#[ensures((result == VoterSetWireDecision::Accept) == (
79 record_version@ == 0 && voter_count@ > 0 && voter_ids_unique
80))]
81#[must_use]
82pub fn voter_set_wire_decision(
83 record_version: i16,
84 voter_count: usize,
85 voter_ids_unique: bool,
86) -> VoterSetWireDecision {
87 if record_version != 0 {
88 VoterSetWireDecision::UnsupportedRecordVersion
89 } else if voter_count == 0 {
90 VoterSetWireDecision::Empty
91 } else if !voter_ids_unique {
92 VoterSetWireDecision::DuplicateId
93 } else {
94 VoterSetWireDecision::Accept
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use {
101 super::{
102 VoterSetWireDecision, VoterWireDecision, voter_set_wire_decision, voter_wire_decision,
103 },
104 assert2::check,
105 };
106
107 #[yare::parameterized(
108 zero_id = { 0, true, 1, true, 0, 0, VoterWireDecision::Accept },
109 maximum_id = { i32::MAX, true, usize::MAX, true, 0, i16::MAX, VoterWireDecision::Accept },
110 negative_id = { -1, true, 1, true, 0, 0, VoterWireDecision::NegativeId },
111 directory_mismatch = { 0, false, 1, true, 0, 0, VoterWireDecision::DirectoryMismatch },
112 zero_endpoints = { 0, true, 0, true, 0, 0, VoterWireDecision::InvalidEndpoint },
113 invalid_endpoints = { 0, true, 1, false, 0, 0, VoterWireDecision::InvalidEndpoint },
114 negative_minimum = { 0, true, 1, true, -1, 0, VoterWireDecision::InvalidVersionRange },
115 negative_maximum = { 0, true, 1, true, 0, -1, VoterWireDecision::InvalidVersionRange },
116 inverted_range = { 0, true, 1, true, 1, 0, VoterWireDecision::InvalidVersionRange },
117 )]
118 fn voter_admission_classifies_every_invalid_boundary(
119 id: i32,
120 directory: bool,
121 count: usize,
122 endpoints: bool,
123 min: i16,
124 max: i16,
125 expected: VoterWireDecision,
126 ) {
127 check!(voter_wire_decision(id, directory, count, endpoints, min, max) == expected);
128 }
129
130 #[yare::parameterized(
131 one_member = { 0, 1, true, VoterSetWireDecision::Accept },
132 maximum_count = { 0, usize::MAX, true, VoterSetWireDecision::Accept },
133 negative_version = { -1, 1, true, VoterSetWireDecision::UnsupportedRecordVersion },
134 newer_version = { 1, 1, true, VoterSetWireDecision::UnsupportedRecordVersion },
135 empty = { 0, 0, true, VoterSetWireDecision::Empty },
136 duplicate_id = { 0, 2, false, VoterSetWireDecision::DuplicateId },
137 )]
138 fn voter_set_admission_requires_version_zero_and_unique_members(
139 version: i16,
140 count: usize,
141 unique: bool,
142 expected: VoterSetWireDecision,
143 ) {
144 check!(voter_set_wire_decision(version, count, unique) == expected);
145 }
146}