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

            
149
426854
        let mut ip = None;
150
426854
        let a = self
151
426854
            .addrs
152
426854
            .iter()
153
461004
            .filter_map(|a| match a {
154
452465
                SocketAddr::V4(a) if ip.is_none() => {
155
452465
                    ip = Some(a);
156
452465
                    None
157
                }
158
375
                other => Some(*other),
159
452840
            })
160
426854
            .collect::<Vec<_>>();
161
426854
        let ip = ip.ok_or_else(|| Error::CannotBuild("No IPv4 address"))?;
162

            
163
        ns_choose! { (
164
            let r_doc_digest = doc_digest;
165
            let m_doc_digest = NotPresent;
166
        ) (
167
426854
            let r_doc_digest = NotPresent;
168
426854
            let m_doc_digest = doc_digest;
169
        ) (
170
            compile_error!("no builder for votes");
171
        ) };
172

            
173
426854
        Ok(RouterStatus {
174
426854
            r: RouterStatusIntroItem {
175
426854
                nickname,
176
426854
                identity: Base64Fingerprint(identity),
177
426854
                doc_digest: r_doc_digest,
178
426854
                publication: IgnoredPublicationTimeSp,
179
426854
                ip: *ip.ip(),
180
426854
                or_port: ip.port(),
181
426854
            },
182
426854
            m: m_doc_digest,
183
426854
            a,
184
426854
            version,
185
426854
            protos,
186
426854
            flags: DocRelayFlags {
187
426854
                known: self.flags,
188
426854
                unknown: Unknown::new_discard(),
189
426854
            },
190
426854
            weight,
191
426854
        })
192
426854
    }
193

            
194
    /// Try to finish this builder and add its RouterStatus to a
195
    /// provided ConsensusBuilder.x
196
426554
    pub fn build_into(&self, builder: &mut ConsensusBuilder) -> Result<()> {
197
426554
        builder.add_rs(self.build()?);
198
426554
        Ok(())
199
426554
    }
200

            
201
    /// Return a router status built by this object.
202
    // TODO this function is identical to `build`; decide which one to keep
203
426854
    pub fn build(&self) -> Result<RouterStatus> {
204
426854
        self.finish()
205
426854
    }
206
}