tor_netdoc/doc/netstatus/build/
each_flavor.rs1ns_use_this_variety! {
14 use [crate::doc::netstatus::rs::build]::?::{RouterStatusBuilder};
15 use [crate::doc::netstatus::rs]::?::{RouterStatus};
16}
17#[cfg(not(doc))]
18ns_use_this_variety! {
19 use [crate::doc::netstatus]::?::{Consensus, Header};
20}
21#[cfg(doc)]
22ns_use_this_variety! {
23 pub use [crate::doc::netstatus]::?::{Consensus, Header};
24}
25
26use super::*;
27
28#[cfg_attr(docsrs, doc(cfg(feature = "build_docs")))]
35pub struct ConsensusBuilder {
36 flavor: ConsensusFlavor,
38 lifetime: Option<Lifetime>,
40 client_versions: Vec<String>,
42 relay_versions: Vec<String>,
44 client_protos: ProtoStatus,
46 relay_protos: ProtoStatus,
48 params: NetParams<i32>,
50 voting_delay: Option<(u32, u32)>,
52 consensus_method: Option<u32>,
54 shared_rand_prev: Option<SharedRandStatus>,
56 shared_rand_cur: Option<SharedRandStatus>,
58 voters: Vec<ConsensusVoterInfo>,
60 relays: Vec<RouterStatus>,
62 weights: NetParams<i32>,
64}
65
66impl ConsensusBuilder {
67 pub(crate) fn new(flavor: ConsensusFlavor) -> ConsensusBuilder {
69 ConsensusBuilder {
70 flavor,
71 lifetime: None,
72 client_versions: Vec::new(),
73 relay_versions: Vec::new(),
74 client_protos: ProtoStatus::default(),
75 relay_protos: ProtoStatus::default(),
76 params: NetParams::new(),
77 voting_delay: None,
78 consensus_method: None,
79 shared_rand_prev: None,
80 shared_rand_cur: None,
81 voters: Vec::new(),
82 relays: Vec::new(),
83 weights: NetParams::new(),
84 }
85 }
86
87 pub fn lifetime(&mut self, lifetime: Lifetime) -> &mut Self {
91 self.lifetime = Some(lifetime);
92 self
93 }
94
95 pub fn add_client_version(&mut self, ver: String) -> &mut Self {
99 self.client_versions.push(ver);
100 self
101 }
102 pub fn add_relay_version(&mut self, ver: String) -> &mut Self {
106 self.relay_versions.push(ver);
107 self
108 }
109 pub fn required_client_protos(&mut self, protos: Protocols) -> &mut Self {
113 self.client_protos.required = protos;
114 self
115 }
116 pub fn recommended_client_protos(&mut self, protos: Protocols) -> &mut Self {
120 self.client_protos.recommended = protos;
121 self
122 }
123 pub fn required_relay_protos(&mut self, protos: Protocols) -> &mut Self {
127 self.relay_protos.required = protos;
128 self
129 }
130 pub fn recommended_relay_protos(&mut self, protos: Protocols) -> &mut Self {
134 self.relay_protos.recommended = protos;
135 self
136 }
137 pub fn param<S>(&mut self, param: S, val: i32) -> &mut Self
139 where
140 S: Into<String>,
141 {
142 self.params.set(param.into(), val);
143 self
144 }
145 pub fn voting_delay(&mut self, vote_delay: u32, signature_delay: u32) -> &mut Self {
147 self.voting_delay = Some((vote_delay, signature_delay));
148 self
149 }
150 pub fn consensus_method(&mut self, consensus_method: u32) -> &mut Self {
154 self.consensus_method = Some(consensus_method);
155 self
156 }
157 pub fn shared_rand_prev(
161 &mut self,
162 n_reveals: u8,
163 value: SharedRandVal,
164 timestamp: Option<SystemTime>,
165 ) -> &mut Self {
166 self.shared_rand_prev = Some(SharedRandStatus {
167 n_reveals,
168 value,
169 timestamp,
170 });
171 self
172 }
173 pub fn shared_rand_cur(
177 &mut self,
178 n_reveals: u8,
179 value: SharedRandVal,
180 timestamp: Option<SystemTime>,
181 ) -> &mut Self {
182 self.shared_rand_cur = Some(SharedRandStatus {
183 n_reveals,
184 value,
185 timestamp,
186 });
187 self
188 }
189 pub fn weight<S>(&mut self, param: S, val: i32) -> &mut Self
191 where
192 S: Into<String>,
193 {
194 self.weights.set(param.into(), val);
195 self
196 }
197 pub fn weights(&mut self, weights: NetParams<i32>) -> &mut Self {
199 self.weights = weights;
200 self
201 }
202 pub fn voter(&self) -> VoterInfoBuilder {
206 VoterInfoBuilder::new()
207 }
208
209 pub(crate) fn add_rs(&mut self, rs: RouterStatus) -> &mut Self {
211 self.relays.push(rs);
212 self
213 }
214}
215
216impl ConsensusBuilder {
217 pub fn rs(&self) -> RouterStatusBuilder {
222 RouterStatusBuilder::new()
223 }
224
225 pub fn testing_consensus(&self) -> Result<Consensus> {
231 let lifetime = self
232 .lifetime
233 .as_ref()
234 .ok_or(Error::CannotBuild("Missing lifetime."))?
235 .clone();
236
237 let proto_statuses = Arc::new(ProtoStatuses {
238 client: self.client_protos.clone(),
239 relay: self.relay_protos.clone(),
240 });
241
242 let consensus_method = self
243 .consensus_method
244 .ok_or(Error::CannotBuild("Missing consensus method."))?;
245
246 let header = Header {
247 flavor: self.flavor,
248 lifetime,
249 client_versions: self.client_versions.clone(),
250 relay_versions: self.relay_versions.clone(),
251 proto_statuses,
252 params: self.params.clone(),
253 voting_delay: self.voting_delay,
254 consensus_method,
255 shared_rand_prev: self.shared_rand_prev.clone(),
256 shared_rand_cur: self.shared_rand_cur.clone(),
257 };
258
259 let footer = Footer {
260 weights: self.weights.clone(),
261 };
262
263 let mut relays = self.relays.clone();
264 relays.sort_by_key(|r| *r.rsa_identity());
265 Ok(Consensus {
268 header,
269 voters: self.voters.clone(),
270 relays,
271 footer,
272 })
273 }
274}
275
276pub struct VoterInfoBuilder {
278 nickname: Option<String>,
280 identity: Option<RsaIdentity>,
282 ip: Option<IpAddr>,
284 contact: Option<String>,
286 vote_digest: Vec<u8>,
288 or_port: u16,
290 dir_port: u16,
292}
293
294impl VoterInfoBuilder {
295 pub(crate) fn new() -> Self {
297 VoterInfoBuilder {
298 nickname: None,
299 identity: None,
300 ip: None,
301 contact: None,
302 vote_digest: Vec::new(),
303 or_port: 0,
304 dir_port: 0,
305 }
306 }
307
308 pub fn nickname(&mut self, nickname: String) -> &mut Self {
312 self.nickname = Some(nickname);
313 self
314 }
315
316 pub fn identity(&mut self, identity: RsaIdentity) -> &mut Self {
320 self.identity = Some(identity);
321 self
322 }
323
324 pub fn ip(&mut self, ip: IpAddr) -> &mut Self {
328 self.ip = Some(ip);
329 self
330 }
331
332 pub fn contact(&mut self, contact: String) -> &mut Self {
336 self.contact = Some(contact);
337 self
338 }
339
340 pub fn vote_digest(&mut self, vote_digest: Vec<u8>) -> &mut Self {
344 self.vote_digest = vote_digest;
345 self
346 }
347
348 pub fn or_port(&mut self, or_port: u16) -> &mut Self {
350 self.or_port = or_port;
351 self
352 }
353
354 pub fn dir_port(&mut self, dir_port: u16) -> &mut Self {
356 self.dir_port = dir_port;
357 self
358 }
359
360 pub fn build(&self, builder: &mut ConsensusBuilder) -> Result<()> {
363 let nickname = self
364 .nickname
365 .as_ref()
366 .ok_or(Error::CannotBuild("Missing nickname"))?
367 .clone();
368 let identity = self
369 .identity
370 .ok_or(Error::CannotBuild("Missing identity"))?;
371 let ip = self.ip.ok_or(Error::CannotBuild("Missing IP"))?;
372 let contact = self
373 .contact
374 .as_ref()
375 .ok_or(Error::CannotBuild("Missing contact"))?
376 .clone();
377 if self.vote_digest.is_empty() {
378 return Err(Error::CannotBuild("Missing vote digest"));
379 }
380 let dir_source = DirSource {
381 nickname,
382 identity,
383 ip,
384 dir_port: self.dir_port,
385 or_port: self.or_port,
386 };
387
388 let info = ConsensusVoterInfo {
389 dir_source,
390 contact,
391 vote_digest: self.vote_digest.clone(),
392 };
393 builder.voters.push(info);
394 Ok(())
395 }
396}
397
398#[cfg(test)]
399mod test {
400 #![allow(clippy::bool_assert_comparison)]
402 #![allow(clippy::clone_on_copy)]
403 #![allow(clippy::dbg_macro)]
404 #![allow(clippy::mixed_attributes_style)]
405 #![allow(clippy::print_stderr)]
406 #![allow(clippy::print_stdout)]
407 #![allow(clippy::single_char_pattern)]
408 #![allow(clippy::unwrap_used)]
409 #![allow(clippy::unchecked_duration_subtraction)]
410 #![allow(clippy::useless_vec)]
411 #![allow(clippy::needless_pass_by_value)]
412 use super::*;
414 use crate::doc::netstatus::RelayFlags;
415
416 use std::net::SocketAddr;
417 use std::time::{Duration, SystemTime};
418
419 #[test]
420 fn consensus() {
421 let now = SystemTime::now();
422 let one_hour = Duration::new(3600, 0);
423
424 let mut builder = crate::doc::netstatus::MdConsensus::builder();
425 builder
426 .lifetime(Lifetime::new(now, now + one_hour, now + 2 * one_hour).unwrap())
427 .add_client_version("0.4.5.8".into())
428 .add_relay_version("0.4.5.9".into())
429 .required_client_protos("DirCache=2 LinkAuth=3".parse().unwrap())
430 .required_relay_protos("DirCache=1".parse().unwrap())
431 .recommended_client_protos("DirCache=6".parse().unwrap())
432 .recommended_relay_protos("DirCache=5".parse().unwrap())
433 .param("wombat", 7)
434 .param("knish", 1212)
435 .voting_delay(7, 8)
436 .consensus_method(32)
437 .shared_rand_prev(1, SharedRandVal([b'x'; 32]), None)
438 .shared_rand_cur(1, SharedRandVal([b'y'; 32]), None)
439 .weight("Wxy", 303)
440 .weight("Wow", 999);
441
442 builder
443 .voter()
444 .nickname("Fuzzy".into())
445 .identity([15; 20].into())
446 .ip("10.0.0.200".parse().unwrap())
447 .contact("admin@fuzzy.example.com".into())
448 .vote_digest((*b"1234").into())
449 .or_port(9001)
450 .dir_port(9101)
451 .build(&mut builder)
452 .unwrap();
453
454 builder
455 .rs()
456 .nickname("Fred".into())
457 .identity([155; 20].into())
458 .add_or_port(SocketAddr::from(([10, 0, 0, 60], 9100)))
459 .add_or_port("[f00f::1]:9200".parse().unwrap())
460 .doc_digest([99; 32])
461 .set_flags(RelayFlags::FAST)
462 .add_flags(RelayFlags::STABLE | RelayFlags::V2DIR)
463 .version("Arti 0.0.0".into())
464 .protos("DirCache=7".parse().unwrap())
465 .build_into(&mut builder)
466 .unwrap();
467
468 let _cons = builder.testing_consensus().unwrap();
469
470 }
472}