1
//! Consensus parameters for stream flow control.
2

            
3
/// Parameters from the consensus that are required for stream flow control.
4
// We want an exhaustive struct with public fields here. If we add a field here, it's probably
5
// because we need it. A builder pattern would mean that the user's code would fail at runtime if
6
// they didn't provide a parameter, rather than at compile time. There is also no `Default` for this
7
// struct as the defaults belong in `NetParameters`, so a non-exhaustive struct would not be able to
8
// be constructed.
9
#[derive(Clone, Debug)]
10
#[allow(clippy::exhaustive_structs)]
11
pub struct FlowCtrlParameters {
12
    /// See `tor_netdir::params::NetParameters::cc_xoff_client`.
13
    // This conversion rate is copied from c-tor (see `flow_control_new_consensus_params()`).
14
    // TODO: This const conversion rate becomes part of the public API, but it shouldn't be. The
15
    // alternative is a bunch of boilerplate code to hide it, so just leaving for now since
16
    // tor-proto is not stable.
17
    pub cc_xoff_client: CellCount<{ tor_cell::relaycell::PAYLOAD_MAX_SIZE_ALL as u32 }>,
18
    /// See `tor_netdir::params::NetParameters::cc_xoff_exit`.
19
    // This conversion rate is copied from c-tor (see `flow_control_new_consensus_params()`).
20
    pub cc_xoff_exit: CellCount<{ tor_cell::relaycell::PAYLOAD_MAX_SIZE_ALL as u32 }>,
21
    /// See `tor_netdir::params::NetParameters::cc_xon_rate`.
22
    // This conversion rate is copied from c-tor (see `flow_control_new_consensus_params()`).
23
    pub cc_xon_rate: CellCount<{ tor_cell::relaycell::PAYLOAD_MAX_SIZE_ANY as u32 }>,
24
    /// See `tor_netdir::params::NetParameters::cc_xon_change_pct`.
25
    pub cc_xon_change_pct: u32,
26
    /// See `tor_netdir::params::NetParameters::cc_xon_ewma_cnt`.
27
    pub cc_xon_ewma_cnt: u32,
28
}
29

            
30
impl FlowCtrlParameters {
31
    #[cfg(test)]
32
390
    pub(crate) fn defaults_for_tests() -> Self {
33
        // These have been copied from the current consensus, but may be out of date.
34
390
        Self {
35
390
            cc_xoff_client: CellCount::new(500),
36
390
            cc_xoff_exit: CellCount::new(500),
37
390
            cc_xon_rate: CellCount::new(500),
38
390
            cc_xon_change_pct: 25,
39
390
            cc_xon_ewma_cnt: 2,
40
390
        }
41
390
    }
42
}
43

            
44
/// A cell count that can be converted into a byte count using a constant conversion rate.
45
///
46
/// The const generic is the conversion multiplier when converting from cells to bytes.
47
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
48
pub struct CellCount<const BYTES_PER_CELL: u32>(u32);
49

            
50
impl<const BYTES_PER_CELL: u32> CellCount<BYTES_PER_CELL> {
51
    /// A new [`CellCount`].
52
1254
    pub const fn new(cells: u32) -> Self {
53
1254
        Self(cells)
54
1254
    }
55

            
56
    /// The [`CellCount`] as the number of cells.
57
    ///
58
    /// This is the value that [`CellCount`] was originally constructed with.
59
    pub const fn as_cells(&self) -> u32 {
60
        self.0
61
    }
62

            
63
    /// The number of payload bytes corresponding to this [`CellCount`].
64
    ///
65
    /// This is a constant multiple of the cell count,
66
    /// and is the conversion we use for the consensus parameters.
67
    /// For example `cc_xoff_client` which says:
68
    ///
69
    /// > Specifies the outbuf length, in relay cell multiples
70
168
    pub const fn as_bytes(&self) -> u64 {
71
        // u32 to u64 cast
72
168
        let cells = self.0 as u64;
73

            
74
168
        cells
75
            // u32 to u64 cast
76
168
            .checked_mul(BYTES_PER_CELL as u64)
77
168
            .expect("u32 * u32 should fit within a u64")
78
168
    }
79
}
80

            
81
#[cfg(test)]
82
mod test {
83
    use super::*;
84

            
85
    #[test]
86
    fn compare_to_ctor_values() {
87
        let params = FlowCtrlParameters {
88
            cc_xoff_client: CellCount::new(1),
89
            cc_xoff_exit: CellCount::new(1),
90
            cc_xon_rate: CellCount::new(1),
91
            cc_xon_change_pct: 1,
92
            cc_xon_ewma_cnt: 1,
93
        };
94

            
95
        // If any of these assertions fail in the future,
96
        // it means that the value no longer matches with C-tor
97
        // `RELAY_PAYLOAD_SIZE_MIN`/`RELAY_PAYLOAD_SIZE_MAX`.
98
        // If this happens we should re-evaluate the status of things and see if we should hard-code
99
        // this to be the same as C-tor, or remove this check.
100

            
101
        /// `RELAY_PAYLOAD_SIZE_MIN` from c-tor
102
        const C_TOR_RELAY_PAYLOAD_SIZE_MIN: u64 = 509 - (16 + 1 + 2 + 2);
103
        /// `RELAY_PAYLOAD_SIZE_MAX` from c-tor
104
        const C_TOR_RELAY_PAYLOAD_SIZE_MAX: u64 = 509 - (1 + 2 + 2 + 4 + 2);
105

            
106
        assert_eq!(
107
            params.cc_xoff_client.as_bytes(),
108
            C_TOR_RELAY_PAYLOAD_SIZE_MIN,
109
        );
110
        assert_eq!(params.cc_xoff_exit.as_bytes(), C_TOR_RELAY_PAYLOAD_SIZE_MIN);
111
        assert_eq!(params.cc_xon_rate.as_bytes(), C_TOR_RELAY_PAYLOAD_SIZE_MAX);
112
    }
113
}