1
//! Calculate consensus preamble
2

            
3
use super::*;
4

            
5
impl ConsensusesFromVotes<()> for netstatus::vote::NetworkStatus {
6
    type PlainOutput = netstatus::plain::NetworkStatus;
7
    type MdOutput = netstatus::md::NetworkStatus;
8

            
9
    fn consensuses<'i>(
10
        context: ConsensusContextRefs<()>,
11
        inputs: impl Iterator<Item = (VoterNum, &'i Self)> + Clone,
12
    ) -> Result<(Self::PlainOutput, Self::MdOutput), ConsensusError>
13
    where
14
        Self: 'i,
15
    {
16
        calc! { both.preamble }
17

            
18
        let context = ConsensusContextRefs {
19
            context: *context,
20
            computed: &plain_preamble,
21
        };
22

            
23
        calc! { both.routers }
24
        calc! { both.authority }
25

            
26
        calc! { both.footer = todo() }
27

            
28
        Ok(construct_both! {
29
            netstatus::plain::NetworkStatus, netstatus::md::NetworkStatus {
30
                both. authority, footer, preamble;
31
            } {
32
                both. routers;
33
                // TODO DIRAUTH NetworkStatus fields missing
34
            }
35
        })
36
    }
37
}
38

            
39
impl<AC> Aggregate<AC> for netstatus::VoteAuthoritySection {
40
    type Output = netstatus::ConsensusAuthoritySection;
41

            
42
    fn aggregate<'i>(
43
        context: ConsensusContextRefs<AC>,
44
        inputs: impl ComponentInVotes<&'i Self>,
45
    ) -> Result<Self::Output, ConsensusError>
46
    where
47
        Self: 'i,
48
    {
49
        use netstatus::{
50
            ConsensusAuthorityEntry, ConsensusAuthorityEntryConstructor, ConsensusAuthoritySection,
51
            ConsensusAuthoritySectionConstructor, DirectorySignaturesHashesAccu,
52
            SupersededAuthorityKey, VoteAuthorityEntry,
53
        };
54

            
55
        fn vote2consensus(
56
            vae: &VoteAuthorityEntry,
57
            sig_hashes: &DirectorySignaturesHashesAccu,
58
        ) -> Result<(ConsensusAuthorityEntry, Option<SupersededAuthorityKey>), Bug> {
59
            macro_rules! copy_field { { $f:ident } => { let $f = vae.$f.clone(); } }
60
            copy_field! { dir_source }
61
            copy_field! { contact }
62

            
63
            let vote_digest = chain!(
64
                sig_hashes.sha256.as_ref().map(|h| &h[..]),
65
                sig_hashes.sha1.as_ref().map(|h| &h[..]),
66
            )
67
            .next()
68
            .ok_or_else(|| internal!("vote without any digest"))?
69
            .to_owned()
70
            .into();
71

            
72
            let superseded_authority_key = vae.legacy_dir_key.map(|fingerprint| {
73
                SupersededAuthorityKey::from_dir_source_and_key(
74
                    //
75
                    vae.dir_source.clone(),
76
                    fingerprint,
77
                )
78
            });
79

            
80
            Ok((
81
                ConsensusAuthorityEntry {
82
                    ..ConsensusAuthorityEntryConstructor {
83
                        dir_source,
84
                        contact,
85
                        vote_digest,
86
                    }
87
                    .construct()
88
                },
89
                superseded_authority_key,
90
            ))
91
        }
92

            
93
        let (authorities, superseded_keys) = inputs
94
            .map(|(vnum, vas)| {
95
                let (cae, lkey) = vote2consensus(
96
                    &vas.authority,
97
                    context
98
                        .sig_hashes
99
                        .get(vnum)
100
                        .ok_or_else(|| internal!("{vnum:?}"))?,
101
                )?;
102
                Ok::<_, Bug>((Some(cae), lkey))
103
            })
104
            .process_results(|i| i.filter_collect_unzip())?;
105

            
106
        Ok(ConsensusAuthoritySection {
107
            superseded_keys,
108
            ..ConsensusAuthoritySectionConstructor { authorities }.construct()
109
        })
110
    }
111
}
112

            
113
/// Computes a consensus from the input votes
114
///
115
/// ### Security
116
///
117
/// It is the caller's responsibility to check that each of the `votes`
118
/// came from a recognised authority.
119
pub fn compute_consensus_from_votes<'v>(
120
    method: SupportedConsensusMethod,
121
    n_authorities: usize,
122
    votes: impl Iterator<
123
        Item = (
124
            &'v netstatus::vote::NetworkStatus,
125
            &'v netstatus::DirectorySignaturesHashesAccu,
126
        ),
127
    >,
128
) -> Result<
129
    (
130
        netstatus::plain::NetworkStatus,
131
        netstatus::md::NetworkStatus,
132
    ),
133
    ConsensusError,
134
> {
135
    let (votes, sig_hashes) = votes.collect();
136
    let context = ConsensusCommonContext {
137
        method,
138
        n_authorities,
139
        votes,
140
        sig_hashes,
141
    };
142
    let context = ConsensusContextRefs {
143
        context: &context,
144
        computed: &(),
145
    };
146
    <netstatus::vote::NetworkStatus as ConsensusesFromVotes<()>>::consensuses(
147
        context,
148
        context
149
            .votes
150
            .iter_enumerated()
151
            .map(|(vnum, vote)| (vnum, *vote)),
152
    )
153
}