qualia_core_db/modalities/
consensus.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum TxStatus {
13 Suspended,
15 Committed,
17}
18
19pub fn transaction_status(parties_assented: usize, total_parties: usize) -> TxStatus {
22 if total_parties > 0 && parties_assented >= total_parties {
23 TxStatus::Committed
24 } else {
25 TxStatus::Suspended
26 }
27}
28
29#[inline]
32pub fn is_globally_valid(local_valid: bool, synced: bool) -> bool {
33 local_valid && synced
34}
35
36#[inline]
39pub fn survives_partition(established_before_partition: bool) -> bool {
40 established_before_partition
41}
42
43#[inline]
46pub fn can_form_joint_during_partition(partitioned: bool) -> bool {
47 !partitioned
48}
49
50#[inline]
55pub fn bft_max_faults(n: usize) -> usize {
56 if n == 0 {
57 0
58 } else {
59 (n - 1) / 3
60 }
61}
62
63#[inline]
66pub fn bft_quorum(n: usize) -> usize {
67 2 * bft_max_faults(n) + 1
68}
69
70#[inline]
72pub fn bft_committed(n: usize, votes: usize) -> bool {
73 n > 0 && votes >= bft_quorum(n)
74}
75
76#[inline]
80pub fn lamport_tick(clock: u64) -> u64 {
81 clock.saturating_add(1)
82}
83
84#[inline]
86pub fn lamport_recv(local: u64, msg: u64) -> u64 {
87 local.max(msg).saturating_add(1)
88}
89
90pub fn vc_happens_before(a: &[u64], b: &[u64]) -> bool {
92 if a.len() != b.len() {
93 return false;
94 }
95 let mut strictly_less = false;
96 for i in 0..a.len() {
97 if a[i] > b[i] {
98 return false;
99 }
100 if a[i] < b[i] {
101 strictly_less = true;
102 }
103 }
104 strictly_less
105}
106
107pub fn vc_concurrent(a: &[u64], b: &[u64]) -> bool {
109 a.len() == b.len() && a != b && !vc_happens_before(a, b) && !vc_happens_before(b, a)
110}
111
112pub fn vc_merge(a: &[u64], b: &[u64], out: &mut [u64]) -> bool {
115 if a.len() != b.len() || out.len() < a.len() {
116 return false;
117 }
118 for i in 0..a.len() {
119 out[i] = a[i].max(b[i]);
120 }
121 true
122}
123
124pub fn is_active_validator(
131 validator_idx: usize,
132 epoch: u64,
133 set_size: usize,
134 active_size: usize,
135) -> bool {
136 if set_size == 0 || active_size == 0 || validator_idx >= set_size {
137 return false;
138 }
139 let active = active_size.min(set_size);
140 let start = (epoch as usize) % set_size;
141 let offset = (validator_idx + set_size - start) % set_size;
143 offset < active
144}
145
146#[inline]
151pub fn is_equivocation(height_a: u64, value_a: u64, height_b: u64, value_b: u64) -> bool {
152 height_a == height_b && value_a != value_b
153}
154
155#[inline]
160pub fn light_client_accepts(proof_verified: bool, n: usize, claimed_votes: usize) -> bool {
161 proof_verified && bft_committed(n, claimed_votes)
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn bft_quorum_tolerates_a_third_faulty() {
170 assert_eq!(bft_max_faults(4), 1);
172 assert_eq!(bft_quorum(4), 3);
173 assert!(bft_committed(4, 3));
174 assert!(!bft_committed(4, 2), "2 votes is below the quorum");
175 assert_eq!(bft_max_faults(7), 2);
177 assert_eq!(bft_quorum(7), 5);
178 }
179
180 #[test]
181 fn lamport_and_vector_clocks() {
182 assert_eq!(lamport_tick(4), 5);
183 assert_eq!(lamport_recv(4, 9), 10); assert!(vc_happens_before(&[1, 0, 0], &[1, 1, 0]));
186 assert!(!vc_happens_before(&[1, 1, 0], &[1, 0, 0]));
187 assert!(vc_concurrent(&[1, 0], &[0, 1]));
189 assert!(!vc_concurrent(&[1, 0], &[1, 1]));
190 let mut out = [0u64; 3];
192 assert!(vc_merge(&[1, 3, 0], &[2, 1, 5], &mut out));
193 assert_eq!(out, [2, 3, 5]);
194 }
195
196 #[test]
197 fn equivocation_and_zk_light_client() {
198 assert!(is_equivocation(10, 0xAA, 10, 0xBB));
200 assert!(
201 !is_equivocation(10, 0xAA, 11, 0xBB),
202 "different heights → not equivocation"
203 );
204 assert!(
205 !is_equivocation(10, 0xAA, 10, 0xAA),
206 "same value → just a re-vote"
207 );
208 assert!(light_client_accepts(true, 4, 3));
210 assert!(!light_client_accepts(false, 4, 3), "no proof → reject");
211 assert!(!light_client_accepts(true, 4, 2), "below quorum → reject");
212 }
213
214 #[test]
215 fn validator_set_rotates_per_epoch() {
216 assert!(is_active_validator(0, 0, 5, 3)); assert!(is_active_validator(2, 0, 5, 3));
219 assert!(!is_active_validator(3, 0, 5, 3));
220 assert!(is_active_validator(3, 1, 5, 3));
222 assert!(!is_active_validator(0, 1, 5, 3));
223 assert!(!is_active_validator(9, 0, 5, 3));
225 assert!(!is_active_validator(0, 0, 0, 3));
226 }
227
228 #[test]
229 fn multi_party_commits_only_on_full_consensus() {
230 assert_eq!(transaction_status(3, 3), TxStatus::Committed);
231 assert_eq!(transaction_status(2, 3), TxStatus::Suspended);
232 assert_eq!(transaction_status(0, 0), TxStatus::Suspended); }
234
235 #[test]
236 fn local_validity_is_not_global() {
237 assert!(
238 !is_globally_valid(true, false),
239 "valid locally but unsynced → not global"
240 );
241 assert!(is_globally_valid(true, true));
242 assert!(!is_globally_valid(false, true));
243 }
244
245 #[test]
246 fn partition_preserves_standing_duties_but_pauses_new_ones() {
247 assert!(survives_partition(true));
249 assert!(!survives_partition(false));
250 assert!(!can_form_joint_during_partition(true));
252 assert!(can_form_joint_during_partition(false));
253 }
254}