• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fjarri / manul / 13248849455

10 Feb 2025 07:27PM UTC coverage: 73.51% (+1.2%) from 72.336%
13248849455

push

github

web-flow
Merge pull request #90 from fjarri/optimize-echo-broadcast

Send hashes in the echo round instead of the full messages

144 of 158 new or added lines in 7 files covered. (91.14%)

11 existing lines in 5 files now uncovered.

2281 of 3103 relevant lines covered (73.51%)

207.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

59.78
/manul/src/session/transcript.rs
1
use alloc::{
2
    collections::{btree_map::Entry, BTreeMap, BTreeSet},
3
    format,
4
    string::String,
5
    vec::Vec,
6
};
7
use core::fmt::Debug;
8

9
use super::{evidence::Evidence, message::SignedMessagePart, session::SessionParameters, LocalError, RemoteError};
10
use crate::protocol::{DirectMessage, EchoBroadcast, NormalBroadcast, Protocol, RoundId};
11

12
#[derive(Debug)]
13
pub(crate) struct Transcript<P: Protocol<SP::Verifier>, SP: SessionParameters> {
14
    echo_broadcasts: BTreeMap<RoundId, BTreeMap<SP::Verifier, SignedMessagePart<EchoBroadcast>>>,
15
    normal_broadcasts: BTreeMap<RoundId, BTreeMap<SP::Verifier, SignedMessagePart<NormalBroadcast>>>,
16
    direct_messages: BTreeMap<RoundId, BTreeMap<SP::Verifier, SignedMessagePart<DirectMessage>>>,
17
    provable_errors: BTreeMap<SP::Verifier, Evidence<P, SP>>,
18
    unprovable_errors: BTreeMap<SP::Verifier, RemoteError>,
19
    missing_messages: BTreeMap<RoundId, BTreeSet<SP::Verifier>>,
20
}
21

22
impl<P, SP> Transcript<P, SP>
23
where
24
    P: Protocol<SP::Verifier>,
25
    SP: SessionParameters,
26
{
27
    pub fn new() -> Self {
26✔
28
        Self {
26✔
29
            echo_broadcasts: BTreeMap::new(),
26✔
30
            normal_broadcasts: BTreeMap::new(),
26✔
31
            direct_messages: BTreeMap::new(),
26✔
32
            provable_errors: BTreeMap::new(),
26✔
33
            unprovable_errors: BTreeMap::new(),
26✔
34
            missing_messages: BTreeMap::new(),
26✔
35
        }
26✔
36
    }
26✔
37

38
    #[allow(clippy::too_many_arguments)]
39
    pub fn update(
70✔
40
        self,
70✔
41
        round_id: &RoundId,
70✔
42
        my_echo_broadcast: (SP::Verifier, SignedMessagePart<EchoBroadcast>),
70✔
43
        echo_broadcasts: BTreeMap<SP::Verifier, SignedMessagePart<EchoBroadcast>>,
70✔
44
        normal_broadcasts: BTreeMap<SP::Verifier, SignedMessagePart<NormalBroadcast>>,
70✔
45
        direct_messages: BTreeMap<SP::Verifier, SignedMessagePart<DirectMessage>>,
70✔
46
        provable_errors: BTreeMap<SP::Verifier, Evidence<P, SP>>,
70✔
47
        unprovable_errors: BTreeMap<SP::Verifier, RemoteError>,
70✔
48
        missing_messages: BTreeSet<SP::Verifier>,
70✔
49
    ) -> Result<Self, LocalError> {
70✔
50
        let mut all_echo_broadcasts = self.echo_broadcasts;
70✔
51
        match all_echo_broadcasts.entry(round_id.clone()) {
70✔
52
            Entry::Vacant(entry) => {
70✔
53
                let mut echo_broadcasts = echo_broadcasts;
70✔
54
                let (my_id, echo_broadcast) = my_echo_broadcast;
70✔
55
                echo_broadcasts.insert(my_id, echo_broadcast);
70✔
56
                entry.insert(echo_broadcasts)
70✔
57
            }
58
            Entry::Occupied(_) => {
59
                return Err(LocalError::new(format!(
×
60
                    "An echo-broadcasts entry for {round_id:?} already exists"
×
61
                )))
×
62
            }
63
        };
64

65
        let mut all_normal_broadcasts = self.normal_broadcasts;
70✔
66
        match all_normal_broadcasts.entry(round_id.clone()) {
70✔
67
            Entry::Vacant(entry) => entry.insert(normal_broadcasts),
70✔
68
            Entry::Occupied(_) => {
69
                return Err(LocalError::new(format!(
×
70
                    "A normal-broadcasts entry for {round_id:?} already exists"
×
71
                )))
×
72
            }
73
        };
74

75
        let mut all_direct_messages = self.direct_messages;
70✔
76
        match all_direct_messages.entry(round_id.clone()) {
70✔
77
            Entry::Vacant(entry) => entry.insert(direct_messages),
70✔
78
            Entry::Occupied(_) => {
79
                return Err(LocalError::new(format!(
×
80
                    "A direct messages entry for {round_id:?} already exists"
×
81
                )))
×
82
            }
83
        };
84

85
        let mut all_provable_errors = self.provable_errors;
70✔
86
        for (verifier, error) in provable_errors {
76✔
87
            if all_provable_errors.insert(verifier.clone(), error).is_some() {
6✔
88
                return Err(LocalError::new(format!(
×
89
                    "A provable errors entry for {verifier:?} already exists"
×
90
                )));
×
91
            }
6✔
92
        }
93

94
        let mut all_unprovable_errors = self.unprovable_errors;
70✔
95
        for (verifier, error) in unprovable_errors {
70✔
96
            if all_unprovable_errors.insert(verifier.clone(), error).is_some() {
×
97
                return Err(LocalError::new(format!(
×
98
                    "An unprovable errors entry for {verifier:?} already exists"
×
99
                )));
×
100
            }
×
101
        }
102

103
        let mut all_missing_messages = self.missing_messages;
70✔
104
        match all_missing_messages.entry(round_id.clone()) {
70✔
105
            Entry::Vacant(entry) => {
70✔
106
                if !missing_messages.is_empty() {
70✔
107
                    entry.insert(missing_messages);
2✔
108
                }
68✔
109
            }
110
            Entry::Occupied(_) => {
111
                return Err(LocalError::new(format!(
×
112
                    "A missing messages entry for {round_id:?} already exists"
×
113
                )))
×
114
            }
115
        };
116

117
        Ok(Self {
70✔
118
            echo_broadcasts: all_echo_broadcasts,
70✔
119
            normal_broadcasts: all_normal_broadcasts,
70✔
120
            direct_messages: all_direct_messages,
70✔
121
            provable_errors: all_provable_errors,
70✔
122
            unprovable_errors: all_unprovable_errors,
70✔
123
            missing_messages: all_missing_messages,
70✔
124
        })
70✔
125
    }
70✔
126

127
    pub fn get_echo_broadcast(
×
128
        &self,
×
129
        round_id: &RoundId,
×
130
        from: &SP::Verifier,
×
131
    ) -> Result<SignedMessagePart<EchoBroadcast>, LocalError> {
×
132
        self.echo_broadcasts
×
133
            .get(round_id)
×
134
            .ok_or_else(|| LocalError::new(format!("No echo broadcasts registered for {round_id:?}")))?
×
135
            .get(from)
×
136
            .cloned()
×
137
            .ok_or_else(|| LocalError::new(format!("No echo broadcasts registered for {from:?} in {round_id:?}")))
×
138
    }
×
139

140
    pub fn get_normal_broadcast(
2✔
141
        &self,
2✔
142
        round_id: &RoundId,
2✔
143
        from: &SP::Verifier,
2✔
144
    ) -> Result<SignedMessagePart<NormalBroadcast>, LocalError> {
2✔
145
        self.normal_broadcasts
2✔
146
            .get(round_id)
2✔
147
            .ok_or_else(|| LocalError::new(format!("No normal broadcasts registered for {round_id:?}")))?
2✔
148
            .get(from)
2✔
149
            .cloned()
2✔
150
            .ok_or_else(|| LocalError::new(format!("No normal broadcasts registered for {from:?} in {round_id:?}")))
2✔
151
    }
2✔
152

153
    pub fn get_direct_message(
2✔
154
        &self,
2✔
155
        round_id: &RoundId,
2✔
156
        from: &SP::Verifier,
2✔
157
    ) -> Result<SignedMessagePart<DirectMessage>, LocalError> {
2✔
158
        self.direct_messages
2✔
159
            .get(round_id)
2✔
160
            .ok_or_else(|| LocalError::new(format!("No direct messages registered for {round_id:?}")))?
2✔
161
            .get(from)
2✔
162
            .cloned()
2✔
163
            .ok_or_else(|| LocalError::new(format!("No direct messages registered for {from:?} in {round_id:?}")))
2✔
164
    }
2✔
165

166
    pub fn is_banned(&self, from: &SP::Verifier) -> bool {
135✔
167
        self.provable_errors.contains_key(from) || self.unprovable_errors.contains_key(from)
135✔
168
    }
135✔
169

170
    pub fn echo_broadcasts(
23✔
171
        &self,
23✔
172
        round_id: &RoundId,
23✔
173
    ) -> Result<BTreeMap<SP::Verifier, SignedMessagePart<EchoBroadcast>>, LocalError> {
23✔
174
        self.echo_broadcasts
23✔
175
            .get(round_id)
23✔
176
            .cloned()
23✔
177
            .ok_or_else(|| LocalError::new(format!("Echo-broadcasts for {round_id:?} are not in the transcript")))
23✔
178
    }
23✔
179

180
    pub fn get_other_echo_broadcasts(
2✔
181
        &self,
2✔
182
        round_id: &RoundId,
2✔
183
        except_for: &SP::Verifier,
2✔
184
    ) -> Result<BTreeMap<SP::Verifier, SignedMessagePart<EchoBroadcast>>, LocalError> {
2✔
185
        let mut other_echo_broadcasts =
2✔
186
            self.echo_broadcasts.get(round_id).cloned().ok_or_else(|| {
2✔
NEW
187
                LocalError::new(format!("Echo-broadcasts for {round_id:?} are not in the transcript"))
×
188
            })?;
2✔
189
        other_echo_broadcasts.remove(except_for);
2✔
190
        Ok(other_echo_broadcasts)
2✔
191
    }
2✔
192
}
193

194
/// Possible outcomes of running a session.
195
#[derive(Debug)]
196
pub enum SessionOutcome<Id, P: Protocol<Id>> {
197
    /// The protocol successfully produced a result.
198
    Result(P::Result),
199
    /// The execution stalled because not enough messages were received to finalize the round.
200
    NotEnoughMessages,
201
    /// The execution was terminated by the user.
202
    Terminated,
203
}
204

205
impl<Id, P> SessionOutcome<Id, P>
206
where
207
    P: Protocol<Id>,
208
{
209
    /// Returns a brief description of the outcome.
210
    pub fn brief(&self) -> String {
×
211
        match self {
×
212
            Self::Result(result) => format!("Success ({result:?})"),
×
213
            Self::NotEnoughMessages => "Not enough messages to finalize, terminated".into(),
×
214
            Self::Terminated => "Terminated by the user".into(),
×
215
        }
216
    }
×
217
}
218

219
/// The report of a session execution.
220
#[derive(Debug)]
221
pub struct SessionReport<P: Protocol<SP::Verifier>, SP: SessionParameters> {
222
    /// The session outcome.
223
    pub outcome: SessionOutcome<SP::Verifier, P>,
224
    /// The provable errors collected during the execution, as the evidences that can be published to prove them.
225
    pub provable_errors: BTreeMap<SP::Verifier, Evidence<P, SP>>,
226
    /// The unprovable errors collected during the execution.
227
    pub unprovable_errors: BTreeMap<SP::Verifier, RemoteError>,
228
    /// The nodes that did not send their messages in time for the corresponding round.
229
    pub missing_messages: BTreeMap<RoundId, BTreeSet<SP::Verifier>>,
230
}
231

232
impl<P, SP> SessionReport<P, SP>
233
where
234
    P: Protocol<SP::Verifier>,
235
    SP: SessionParameters,
236
{
237
    pub(crate) fn new(outcome: SessionOutcome<SP::Verifier, P>, transcript: Transcript<P, SP>) -> Self {
26✔
238
        Self {
26✔
239
            outcome,
26✔
240
            provable_errors: transcript.provable_errors,
26✔
241
            unprovable_errors: transcript.unprovable_errors,
26✔
242
            missing_messages: transcript.missing_messages,
26✔
243
        }
26✔
244
    }
26✔
245

246
    /// Returns the protocol result if the session outcome contains it, otherwise `None`.
247
    pub fn result(self) -> Option<P::Result> {
×
248
        match self.outcome {
×
249
            SessionOutcome::Result(result) => Some(result),
×
250
            _ => None,
×
251
        }
252
    }
×
253

254
    /// Returns a brief description of report.
255
    pub fn brief(&self) -> String {
×
256
        let provable_errors_str = if !self.provable_errors.is_empty() {
×
257
            let descriptions = self
×
258
                .provable_errors
×
259
                .iter()
×
260
                .map(|(id, evidence)| format!("  {:?}: {}", id, evidence.description()))
×
261
                .collect::<Vec<_>>();
×
262
            format!("\nProvable errors:\n{}", descriptions.join("\n"))
×
263
        } else {
264
            "".into()
×
265
        };
266

267
        let unprovable_errors_str = if !self.unprovable_errors.is_empty() {
×
268
            let errors = self
×
269
                .unprovable_errors
×
270
                .iter()
×
271
                .map(|(id, error)| format!("  {:?}: {}", id, error))
×
272
                .collect::<Vec<_>>();
×
273
            format!("\nUnprovable errors:\n{}", errors.join("\n"))
×
274
        } else {
275
            "".into()
×
276
        };
277

278
        let missing_messages_str = if !self.missing_messages.is_empty() {
×
279
            let faulty_parties = self
×
280
                .missing_messages
×
281
                .iter()
×
282
                .map(|(round_id, parties)| format!("  {}: {:?}", round_id, parties))
×
283
                .collect::<Vec<_>>();
×
284
            format!("\nMissing messages:\n{}", faulty_parties.join("\n"))
×
285
        } else {
286
            "".into()
×
287
        };
288

289
        format!(
×
290
            "Result: {}{provable_errors_str}{unprovable_errors_str}{missing_messages_str}",
×
291
            self.outcome.brief(),
×
292
        )
×
293
    }
×
294
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc