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
453190
    pub(crate) fn new() -> Self {
45
453190
        RouterStatusBuilder {
46
453190
            nickname: None,
47
453190
            identity: None,
48
453190
            addrs: Vec::new(),
49
453190
            doc_digest: None,
50
453190
            flags: RelayFlags::empty(),
51
453190
            version: None,
52
453190
            protos: None,
53
453190
            weight: None,
54
453190
        }
55
453190
    }
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
455230
    pub fn identity(&mut self, identity: RsaIdentity) -> &mut Self {
71
455230
        self.identity = Some(identity);
72
455230
        self
73
455230
    }
74
    /// Add an OrPort at `addr` to this routerstatus.
75
    ///
76
    /// At least one value here is required.
77
453551
    pub fn add_or_port(&mut self, addr: SocketAddr) -> &mut Self {
78
453551
        self.addrs.push(addr);
79
453551
        self
80
453551
    }
81
    /// Set the document digest for this routerstatus.
82
    ///
83
    /// This value is required.
84
435391
    pub fn doc_digest(&mut self, doc_digest: DocDigest) -> &mut Self {
85
435391
        self.doc_digest = Some(doc_digest);
86
435391
        self
87
435391
    }
88
    /// Replace the current flags in this routerstatus with `flags`.
89
420224
    pub fn set_flags(&mut self, flags: impl Into<RelayFlags>) -> &mut Self {
90
420224
        self.flags = flags.into();
91
420224
        self
92
420224
    }
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
454720
    pub fn protos(&mut self, protos: Protocols) -> &mut Self {
117
454720
        self.protos = Some(protos);
118
454720
        self
119
454720
    }
120
    /// Set the weight of this routerstatus for random selection.
121
    ///
122
    /// This value is optional; it defaults to 0.
123
463998
    pub fn weight(&mut self, weight: RelayWeight) -> &mut Self {
124
463998
        self.weight = Some(weight);
125
463998
        self
126
463998
    }
127
    /// Try to build a GenericRouterStatus from this builder.
128
    // TODO this function is identical to `build`; decide which one to keep
129
435391
    pub(super) fn finish(&self) -> Result<RouterStatus> {
130
435391
        let nickname = self.nickname.as_deref().unwrap_or("Unnamed").parse()
131
435391
            .map_err(|_| Error::CannotBuild("Invalid nickname"))?;
132
435391
        let identity = self
133
435391
            .identity
134
435391
            .ok_or(Error::CannotBuild("Missing RSA identity"))?;
135
435391
        if self.addrs.is_empty() {
136
            return Err(Error::CannotBuild("No addresses"));
137
435391
        }
138
435391
        let doc_digest = *self
139
435391
            .doc_digest
140
435391
            .as_ref()
141
435391
            .ok_or(Error::CannotBuild("Missing document digest"))?;
142
435391
        let protos = self
143
435391
            .protos
144
435391
            .as_ref()
145
435391
            .ok_or(Error::CannotBuild("Missing protocols"))?
146
435391
            .clone();
147
435391
        let weight = self.weight.unwrap_or(RelayWeight::Unmeasured(0));
148
435391
        let weight = RelayWeightsItem {
149
435391
            effective: weight,
150
435391
            params: Unknown::new_discard(),
151
435391
        };
152
435391
        let version = self.version.as_deref().map(str::parse).transpose()?;
153

            
154
435391
        let mut ip = None;
155
435391
        let a = self
156
435391
            .addrs
157
435391
            .iter()
158
469541
            .filter_map(|a| match a {
159
461002
                SocketAddr::V4(a) if ip.is_none() => {
160
461002
                    ip = Some(a);
161
461002
                    None
162
                }
163
382
                other => Some(*other),
164
461384
            })
165
435391
            .collect::<Vec<_>>();
166
435391
        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
435391
            let r_doc_digest = NotPresent;
173
435391
            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
435391
        Ok(RouterStatus {
180
435391
            r: RouterStatusIntroItem {
181
435391
                nickname,
182
435391
                identity: Base64Fingerprint(identity),
183
435391
                doc_digest: r_doc_digest.into(),
184
435391
                publication: IgnoredPublicationTimeSp,
185
435391
                ip: *ip.ip(),
186
435391
                or_port: ip.port(),
187
435391
                dir_port: 0,
188
435391
            },
189
435391
            m: m_doc_digest.into(),
190
435391
            a,
191
435391
            version,
192
435391
            protos,
193
435391
            flags: DocRelayFlags {
194
435391
                known: self.flags,
195
435391
                unknown: Unknown::new_discard(),
196
435391
            },
197
435391
            weight,
198
435391
            port_policy: ns_expr!(None, NotPresent, Void {}),
199
435391
            ed25519_id: NotPresent,
200
435391
            stats: NotPresent,
201
435391
        })
202
435391
    }
203

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

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