1
//! router status entry builders - items that vary by consensus flavor
2
//!
3
//! **This file is reincluded multiple times**,
4
//! by the macros in [`crate::doc::ns_variety_definition_macros`],
5
//! once for votes, and once for each consensus flavour.
6
//! It is *not* a module `crate::doc::netstatus::rs::each_flavor`.
7
//!
8
//! Each time this file is included by one of the macros mentioned above,
9
//! the `ns_***` macros (such as `ns_const_name!`) may expand to different values.
10
//!
11
//! See [`crate::doc::ns_variety_definition_macros`].
12

            
13
use super::*;
14

            
15
ns_use_this_variety! {
16
    use [crate::doc::netstatus::rs]::?::{DocDigest, RouterStatus, RouterStatusIntroItem};
17
}
18

            
19
/// A Builder object for creating a RouterStatus and adding it to a
20
/// consensus.
21
#[cfg_attr(docsrs, doc(cfg(feature = "build_docs")))]
22
#[derive(Debug, Clone)]
23
pub struct RouterStatusBuilder {
24
    /// See [`RouterStatus::nickname`].
25
    nickname: Option<String>,
26
    /// See [`RouterStatusIntroItem::identity`].
27
    identity: Option<RsaIdentity>,
28
    /// See [`RouterStatus::addrs`].
29
    addrs: Vec<SocketAddr>,
30
    /// See [`RouterStatus::doc_digest`].
31
    doc_digest: Option<DocDigest>,
32
    /// See [`RouterStatus::flags`].
33
    flags: RelayFlags,
34
    /// See [`RouterStatus::version`].
35
    version: Option<String>,
36
    /// See [`RouterStatus::protos`].
37
    protos: Option<Protocols>,
38
    /// See [`RouterStatus::weight`].
39
    weight: Option<RelayWeight>,
40
}
41

            
42
impl RouterStatusBuilder {
43
    /// Construct a new RouterStatusBuilder.
44
435418
    pub(crate) fn new() -> Self {
45
435418
        RouterStatusBuilder {
46
435418
            nickname: None,
47
435418
            identity: None,
48
435418
            addrs: Vec::new(),
49
435418
            doc_digest: None,
50
435418
            flags: RelayFlags::empty(),
51
435418
            version: None,
52
435418
            protos: None,
53
435418
            weight: None,
54
435418
        }
55
435418
    }
56

            
57
    /// Set the nickname for this routerstatus.
58
    ///
59
    /// This value defaults to "Unnamed".
60
4
    pub fn nickname(&mut self, nickname: String) -> &mut Self {
61
4
        self.nickname = Some(nickname);
62
4
        self
63
4
    }
64

            
65
    /// Set the RSA identity for this routerstatus.
66
    ///
67
    /// (The Ed25519 identity is in the microdescriptor).
68
    ///
69
    /// This value is required.
70
437378
    pub fn identity(&mut self, identity: RsaIdentity) -> &mut Self {
71
437378
        self.identity = Some(identity);
72
437378
        self
73
437378
    }
74
    /// Add an OrPort at `addr` to this routerstatus.
75
    ///
76
    /// At least one value here is required.
77
435765
    pub fn add_or_port(&mut self, addr: SocketAddr) -> &mut Self {
78
435765
        self.addrs.push(addr);
79
435765
        self
80
435765
    }
81
    /// Set the document digest for this routerstatus.
82
    ///
83
    /// This value is required.
84
418317
    pub fn doc_digest(&mut self, doc_digest: DocDigest) -> &mut Self {
85
418317
        self.doc_digest = Some(doc_digest);
86
418317
        self
87
418317
    }
88
    /// Replace the current flags in this routerstatus with `flags`.
89
403470
    pub fn set_flags(&mut self, flags: impl Into<RelayFlags>) -> &mut Self {
90
403470
        self.flags = flags.into();
91
403470
        self
92
403470
    }
93
    /// Make all the flags in `flags` become set on this routerstatus,
94
    /// in addition to the flags already set.
95
96
    pub fn add_flags(&mut self, flags: impl Into<RelayFlags>) -> &mut Self {
96
96
        self.flags |= flags.into();
97
96
        self
98
96
    }
99
    /// Make all the flags in `flags` become cleared on this routerstatus.
100
    #[cfg(feature = "testing")]
101
476
    pub fn clear_flags(&mut self, flags: impl Into<RelayFlags>) -> &mut Self {
102
476
        self.flags &= !flags.into();
103
476
        self
104
476
    }
105
    /// Set the version of the relay described in this routerstatus.
106
    ///
107
    /// This value is optional.
108
4
    pub fn version(&mut self, version: String) -> &mut Self {
109
4
        self.version = Some(version);
110
4
        self
111
4
    }
112
    /// Set the list of subprotocols supported by the relay described
113
    /// by this routerstatus.
114
    ///
115
    /// This value is required.
116
436888
    pub fn protos(&mut self, protos: Protocols) -> &mut Self {
117
436888
        self.protos = Some(protos);
118
436888
        self
119
436888
    }
120
    /// Set the weight of this routerstatus for random selection.
121
    ///
122
    /// This value is optional; it defaults to 0.
123
445802
    pub fn weight(&mut self, weight: RelayWeight) -> &mut Self {
124
445802
        self.weight = Some(weight);
125
445802
        self
126
445802
    }
127
    /// Try to build a GenericRouterStatus from this builder.
128
    // TODO this function is identical to `build`; decide which one to keep
129
418317
    pub(super) fn finish(&self) -> Result<RouterStatus> {
130
418317
        let nickname = self.nickname.as_deref().unwrap_or("Unnamed").parse()
131
418317
            .map_err(|_| Error::CannotBuild("Invalid nickname"))?;
132
418317
        let identity = self
133
418317
            .identity
134
418317
            .ok_or(Error::CannotBuild("Missing RSA identity"))?;
135
418317
        if self.addrs.is_empty() {
136
            return Err(Error::CannotBuild("No addresses"));
137
418317
        }
138
418317
        let doc_digest = *self
139
418317
            .doc_digest
140
418317
            .as_ref()
141
418317
            .ok_or(Error::CannotBuild("Missing document digest"))?;
142
418317
        let protos = self
143
418317
            .protos
144
418317
            .as_ref()
145
418317
            .ok_or(Error::CannotBuild("Missing protocols"))?
146
418317
            .clone();
147
418317
        let weight = self.weight.unwrap_or(RelayWeight::Unmeasured(0));
148
418317
        let weight = RelayWeightsItem {
149
418317
            effective: weight,
150
418317
            params: Unknown::new_discard(),
151
418317
        };
152
418317
        let version = self.version.as_deref().map(str::parse).transpose()?;
153

            
154
418317
        let mut ip = None;
155
418317
        let a = self
156
418317
            .addrs
157
418317
            .iter()
158
452467
            .filter_map(|a| match a {
159
443928
                SocketAddr::V4(a) if ip.is_none() => {
160
443928
                    ip = Some(a);
161
443928
                    None
162
                }
163
368
                other => Some(*other),
164
444296
            })
165
418317
            .collect::<Vec<_>>();
166
418317
        let ip = ip.ok_or_else(|| Error::CannotBuild("No IPv4 address"))?;
167

            
168
        ns_choose! { (
169
            let r_doc_digest = doc_digest;
170
            let m_doc_digest = NotPresent;
171
        ) (
172
418317
            let r_doc_digest = NotPresent;
173
418317
            let m_doc_digest = doc_digest;
174
        ) (
175
            compile_error!("no builder for votes");
176
        ) };
177

            
178
        #[allow(clippy::useless_conversion)] // sometimes doc_digest needs into
179
418317
        Ok(RouterStatus {
180
418317
            r: RouterStatusIntroItem {
181
418317
                nickname,
182
418317
                identity: Base64Fingerprint(identity),
183
418317
                doc_digest: r_doc_digest.into(),
184
418317
                publication: IgnoredPublicationTimeSp,
185
418317
                ip: *ip.ip(),
186
418317
                or_port: ip.port(),
187
418317
                dir_port: 0,
188
418317
            },
189
418317
            m: m_doc_digest.into(),
190
418317
            a,
191
418317
            version,
192
418317
            protos,
193
418317
            flags: DocRelayFlags {
194
418317
                known: self.flags,
195
418317
                unknown: Unknown::new_discard(),
196
418317
            },
197
418317
            weight,
198
418317
            port_policy: ns_expr!(None, NotPresent, Void {}),
199
418317
            ed25519_id: NotPresent,
200
418317
            stats: NotPresent,
201
418317
        })
202
418317
    }
203

            
204
    /// Try to finish this builder and add its RouterStatus to a
205
    /// provided ConsensusBuilder.x
206
418023
    pub fn build_into(&self, builder: &mut ConsensusBuilder) -> Result<()> {
207
418023
        builder.add_rs(self.build()?);
208
418023
        Ok(())
209
418023
    }
210

            
211
    /// Return a router status built by this object.
212
    // TODO this function is identical to `build`; decide which one to keep
213
418317
    pub fn build(&self) -> Result<RouterStatus> {
214
418317
        self.finish()
215
418317
    }
216
}