1use anyhow::Result;
10use serde::{Deserialize, Serialize};
11use shared_ids::ReplicaId;
12use tracing::{error, trace};
13
14use crate::{error::InnerError, Config, View};
15
16#[derive(Serialize, Deserialize, Clone, Debug)]
19pub(crate) struct ReqViewChange {
20 pub(crate) prev_view: View,
22 pub(crate) next_view: View,
24}
25
26impl ReqViewChange {
27 pub(crate) fn validate(&self, origin: ReplicaId, config: &Config) -> Result<(), InnerError> {
40 trace!(
41 "Validating ReqViewChange (previous view: {:?}, next view: {:?}) ...",
42 self.prev_view,
43 self.next_view
44 );
45 if self.prev_view < self.next_view {
46 trace!(
47 "Successfully validated ReqViewChange (previous view: {:?}, next view: {:?}).",
48 self.prev_view,
49 self.next_view
50 );
51 Ok(())
52 } else {
53 error!(
54 "Failed validating ReqViewChange (previous view: {:?}, next view: {:?}): Previous view set is not smaller than next view set.",
55 self.prev_view, self.next_view
56 );
57 Err(InnerError::ReqViewChangeIncompatiblePrevNextView {
58 receiver: config.id,
59 origin,
60 })
61 }
62 }
63}
64
65#[cfg(test)]
66
67mod tests {
68 use std::num::NonZeroU64;
69
70 use crate::{
71 tests::{
72 create_default_configs_for_replicas, create_random_valid_req_vc_next_dir_subsequent,
73 create_random_valid_req_vc_next_jump, get_random_replica_id,
74 },
75 View,
76 };
77
78 use rand::{thread_rng, Rng};
79
80 use rstest::rstest;
81 use shared_ids::ReplicaId;
82
83 use super::ReqViewChange;
84
85 #[rstest]
92 fn validate_valid_req_view_change_next_dir_subsequent(
93 #[values(3, 4, 5, 6, 7, 8, 9, 10)] n: u64,
94 ) {
95 let n_parsed = NonZeroU64::new(n).unwrap();
96 let mut rng = thread_rng();
97 let origin = get_random_replica_id(n_parsed, &mut rng);
98
99 let t = n / 2;
100
101 let configs = create_default_configs_for_replicas(n_parsed, t);
102
103 let req_view_change = create_random_valid_req_vc_next_dir_subsequent(n_parsed, &mut rng);
104
105 for i in 0..n {
106 let rep_id = ReplicaId::from_u64(i);
107 let config = configs.get(&rep_id).unwrap();
108 assert!((req_view_change.validate(origin, config)).is_ok());
109 }
110 }
111
112 #[rstest]
119 fn validate_valid_req_view_change_next_jump(#[values(3, 4, 5, 6, 7, 8, 9, 10)] n: u64) {
120 let n_parsed = NonZeroU64::new(n).unwrap();
121 let mut rng = thread_rng();
122 let origin = get_random_replica_id(n_parsed, &mut rng);
123
124 let t = n / 2;
125
126 let configs = create_default_configs_for_replicas(n_parsed, t);
127
128 let req_view_change = create_random_valid_req_vc_next_jump(n_parsed, &mut rng);
129
130 for i in 0..n {
131 let rep_id = ReplicaId::from_u64(i);
132 let config = configs.get(&rep_id).unwrap();
133 assert!((req_view_change.validate(origin, config)).is_ok());
134 }
135 }
136
137 #[rstest]
144 fn validate_invalid_req_view_change_prev_dir_subsequent(
145 #[values(3, 4, 5, 6, 7, 8, 9, 10)] n: u64,
146 ) {
147 use rand::thread_rng;
148
149 use crate::tests::{
150 create_default_configs_for_replicas, get_random_included_index, get_random_replica_id,
151 };
152
153 let n_parsed = NonZeroU64::new(n).unwrap();
154 let mut rng = thread_rng();
155 let origin = get_random_replica_id(n_parsed, &mut rng);
156
157 let rand_factor_0 = get_random_included_index(n as usize * 10, None, &mut rng);
158
159 let next_view_nr = rng.gen_range(0..=rand_factor_0 as u64 * n);
160 let prev_view_nr = next_view_nr + 1;
161
162 let t = n / 2;
163
164 let prev_view = View(prev_view_nr);
165 let next_view = View(next_view_nr);
166
167 let configs = create_default_configs_for_replicas(n_parsed, t);
168
169 let req_view_change = ReqViewChange {
170 prev_view,
171 next_view,
172 };
173
174 for i in 0..n {
175 let rep_id = ReplicaId::from_u64(i);
176 let config = configs.get(&rep_id).unwrap();
177 assert!((req_view_change.validate(origin, config)).is_err());
178 }
179 }
180
181 #[rstest]
188 fn validate_invalid_req_view_change_jump(#[values(3, 4, 5, 6, 7, 8, 9, 10)] n: u64) {
189 use rand::thread_rng;
190
191 use crate::tests::{
192 create_default_configs_for_replicas, get_random_included_index, get_random_replica_id,
193 };
194
195 let n_parsed = NonZeroU64::new(n).unwrap();
196 let mut rng = thread_rng();
197 let origin = get_random_replica_id(n_parsed, &mut rng);
198
199 let rand_factor_0 = get_random_included_index(n as usize * 10, None, &mut rng);
200 let rand_summand = rng.gen_range(1..=n * 10);
201
202 let next_view_nr = rng.gen_range(0..=rand_factor_0 as u64 * n);
203 let prev_view_nr = next_view_nr + rand_summand;
204
205 let t = n / 2;
206
207 let prev_view = View(prev_view_nr);
208 let next_view = View(next_view_nr);
209
210 let configs = create_default_configs_for_replicas(n_parsed, t);
211
212 let req_view_change = ReqViewChange {
213 prev_view,
214 next_view,
215 };
216
217 for i in 0..n {
218 let rep_id = ReplicaId::from_u64(i);
219 let config = configs.get(&rep_id).unwrap();
220 assert!((req_view_change.validate(origin, config)).is_err());
221 }
222 }
223}