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 void::ResultVoidExt;
14

            
15
use super::*;
16

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

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

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

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

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

            
165
452463
        let mut ip = None;
166
452463
        let a = self
167
452463
            .addrs
168
452463
            .iter()
169
452463
            .filter_map(|a| match a {
170
452463
                SocketAddr::V4(a) if ip.is_none() => {
171
452463
                    ip = Some(a);
172
452463
                    None
173
                }
174
373
                other => Some(*other),
175
452836
            })
176
452463
            .collect::<Vec<_>>();
177
452463
        let ip = ip.ok_or_else(|| Error::CannotBuild("No IPv4 address"))?;
178

            
179
        ns_choose! { (
180
            let r_doc_digest = doc_digest;
181
            let m_doc_digest = NotPresent;
182
        ) (
183
452463
            let r_doc_digest = NotPresent;
184
452463
            let m_doc_digest = doc_digest;
185
        ) (
186
            compile_error!("no builder for votes");
187
        ) };
188

            
189
        #[allow(clippy::useless_conversion)] // sometimes doc_digest needs into
190
452463
        Ok(RouterStatus {
191
452463
            r: RouterStatusIntroItem {
192
452463
                nickname,
193
452463
                identity: Base64Fingerprint(identity),
194
452463
                doc_digest: r_doc_digest.into(),
195
452463
                publication: IgnoredPublicationTimeSp,
196
452463
                ip: *ip.ip(),
197
452463
                or_port: ip.port(),
198
452463
                dir_port: 0,
199
452463
                __non_exhaustive: (),
200
452463
            },
201
452463
            m: m_doc_digest.into(),
202
452463
            a,
203
452463
            version,
204
452463
            protos,
205
452463
            flags: DocRelayFlags {
206
452463
                known: self.flags,
207
452463
                unknown: Unknown::new_discard(),
208
452463
            },
209
452463
            weight,
210
452463
            port_policy: ns_expr!(None, NotPresent, Void {}),
211
452463
            ed25519_id: NotPresent,
212
452463
            stats: NotPresent,
213
452463
            __non_exhaustive: (),
214
452463
        })
215
452463
    }
216

            
217
    /// Try to finish this builder and add its RouterStatus to a
218
    /// provided ConsensusBuilder.x
219
452145
    pub fn build_into(&self, builder: &mut ConsensusBuilder) -> Result<()> {
220
452145
        builder.add_rs(self.build()?);
221
452145
        Ok(())
222
452145
    }
223

            
224
    /// Return a router status built by this object.
225
    // TODO this function is identical to `build`; decide which one to keep
226
452463
    pub fn build(&self) -> Result<RouterStatus> {
227
452463
        self.finish()
228
452463
    }
229
}