1
//! routerstatus calculation - interface between rs selection and per-rs calculation
2

            
3
use super::*;
4
pub(super) use netstatus::vote::RouterStatus as RouterStatusVote;
5
pub(super) use routerdesc::RdDigest;
6
pub(super) use tor_netdoc::doc::routerdesc;
7

            
8
/// Router id-tuple as used in consensus calculations
9
///
10
/// See
11
/// <https://spec.torproject.org/dir-spec/computing-consensus.html#choosing-relay-ids>
12
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly, derive_more::Display)]
13
#[derive_deftly(DiscrepReportFilter)]
14
#[display("ed={ed} / rsa={rsa}")]
15
pub(super) struct IdTuple {
16
    /// ed25519
17
    pub ed: Ed25519Public,
18

            
19
    /// RSA
20
    pub rsa: Base64Fingerprint,
21
}
22

            
23
/// Router status-tuple: critical info unified as an aggregate, "most voters wins"
24
///
25
/// See
26
/// <https://spec.torproject.org/dir-spec/computing-consensus.html#choosing-relay-descs>
27
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
28
#[derive_deftly(StatusTupleFromRouterStatusVote)]
29
pub(super) struct StatusTuple {
30
    /// routerdesc digest
31
    pub doc_digest: RdDigest,
32
    /// publication field
33
    pub publication: Iso8601TimeSp,
34
    /// nickname
35
    pub nickname: Nickname,
36
    /// ip
37
    pub ip: Ipv4Addr,
38
    /// dir_port
39
    pub dir_port: u16,
40
    /// or_port
41
    pub or_port: u16,
42
}
43

            
44
/// The `RouterStatusVote` provided by each voter that voted for this id-tuple
45
///
46
/// `None` for voters who didn't vote for this id-tuple.
47
pub(super) type RouterStatusPerRelevantVoter<'i> =
48
    TiSlice<VoterNum, Option<&'i netstatus::vote::RouterStatus>>;
49

            
50
/// Input to per-routerstatus routerstatus calculation
51
pub(super) struct ResolvedRouterStatusInputs<'i> {
52
    /// id-tuple
53
    pub id: IdTuple,
54

            
55
    /// status-tuple
56
    pub status_tuple: StatusTuple,
57

            
58
    /// routerstatuses from each vote that voted for this id-tuple
59
    pub per_voter: Box<RouterStatusPerRelevantVoter<'i>>,
60
}
61

            
62
impl IdTuple {
63
    /// Extract the id-tuple from a vote's routerstatus
64
    pub(super) fn from_rs(rs: &RouterStatusVote) -> Self {
65
        IdTuple {
66
            ed: rs.ed25519_id.pk,
67
            rsa: rs.r.identity,
68
        }
69
    }
70
}
71

            
72
define_derive_deftly! {
73
    /// Defines `struct DiscrepReportFilter`
74
    DiscrepReportFilter beta_deftly:
75

            
76
    /// Filter for discrepancy report algorithm
77
    ///
78
    /// Embodies the *chosen* relay identities, so that we can report the *other*
79
    /// relay identities for which we do output a routerdesc mentioning at least one
80
    /// of the relevant identities.
81
    ///
82
    /// Helps implement the logging requirement in
83
    /// <https://spec.torproject.org/dir-spec/computing-consensus.html#choosing-relay-ids>
84
    #[derive(Debug)]
85
    pub(super) struct DiscrepReportFilter<'i> {
86
        output_ids: &'i HashSet<&'i IdTuple>,
87
      $(
88
        $<included_ $fname>: HashSet<&'i $ftype>,
89
      )
90
    }
91

            
92
    impl<'i> DiscrepReportFilter<'i> {
93
        /// Make a new filter from the set of chosen output identities
94
        pub(super) fn from_outputs(output_ids: &'i HashSet<&'i IdTuple>) -> Self {
95
            Self {
96
                output_ids,
97
              $(
98
                $<included_ $fname>:
99
                    output_ids
100
                        .clone()
101
                        .into_iter()
102
                        .map(|id| &id.$fname)
103
                        .collect(),
104
              )
105
            }
106
        }
107

            
108
        /// Is this id discrepant (ie, should it be logged) ?
109
        pub(super) fn id_is_discrepant(&self, id: &IdTuple) -> bool {
110
            if self.output_ids.contains(id) {
111
                return false;
112
            }
113
          $(
114
            self.$<included_$fname>.contains(&id.$fname) ||
115
          )
116
            false
117
        }
118
    }
119
}
120
use derive_deftly_template_DiscrepReportFilter;
121

            
122
define_derive_deftly! {
123
    /// Derives `StatusTuple::from_rs` by copying the corresponding fields
124
    ///
125
    /// The data comes from the intro item `r`; it so happens that all the fields for use
126
    /// in the status-tuple are in the intro item.
127
    StatusTupleFromRouterStatusVote beta_deftly, meta_quoted strip:
128

            
129
    $impl {
130
        /// Extract the `StatusTuple` from a routerstatus
131
        pub(super) fn from_rs(rs: &RouterStatusVote) -> Self {
132
            Self { $(
133
                $fname: rs.r.$fname.clone().into(),
134
            ) }
135
        }
136
    }
137
}
138
use derive_deftly_template_StatusTupleFromRouterStatusVote;