Skip to content

Commit d158efb

Browse files
committed
Introduce IdSet and use it for normal rounds
1 parent 4ac63df commit d158efb

File tree

4 files changed

+62
-25
lines changed

4 files changed

+62
-25
lines changed

manul/src/protocol.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub use errors::{
2626
};
2727
pub use message::{DirectMessage, EchoBroadcast, NormalBroadcast, ProtocolMessage, ProtocolMessagePart};
2828
pub use round::{
29-
Artifact, CommunicationInfo, EchoRoundParticipation, EntryPoint, FinalizeOutcome, NoProtocolErrors, PartyId,
29+
Artifact, CommunicationInfo, EchoRoundParticipation, EntryPoint, FinalizeOutcome, IdSet, NoProtocolErrors, PartyId,
3030
Payload, Protocol, ProtocolError, RequiredMessageParts, RequiredMessages, Round,
3131
};
3232
pub use round_id::{RoundId, TransitionInfo};

manul/src/protocol/round.rs

+35-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,39 @@ use super::{
1919
round_id::{RoundId, TransitionInfo},
2020
};
2121

22+
// TODO: make a trait to implement custom threshold strategies
23+
/// A set of IDs with an associated quorum condition.
24+
#[derive(Debug, Clone)]
25+
pub struct IdSet<Id> {
26+
ids: BTreeSet<Id>,
27+
threshold: usize,
28+
}
29+
30+
impl<Id: Ord> IdSet<Id> {
31+
/// Creates a non-threshold ID set (that is, messages from all `ids` must be present for the quorum).
32+
pub fn new_non_threshold(ids: BTreeSet<Id>) -> Self {
33+
let threshold = ids.len();
34+
Self { ids, threshold }
35+
}
36+
37+
/// Creates an empty ID set.
38+
pub fn empty() -> Self {
39+
Self {
40+
ids: BTreeSet::new(),
41+
threshold: 0,
42+
}
43+
}
44+
45+
pub(crate) fn all(&self) -> &BTreeSet<Id> {
46+
&self.ids
47+
}
48+
49+
pub(crate) fn is_quorum(&self, ids: &BTreeSet<Id>) -> bool {
50+
// TODO: assuming `ids` are a subset of `self.ids`. Can we?
51+
ids.len() >= self.threshold
52+
}
53+
}
54+
2255
/// Describes what other parties this rounds sends messages to, and what other parties it expects messages from.
2356
#[derive(Debug, Clone)]
2457
pub struct CommunicationInfo<Id> {
@@ -35,7 +68,7 @@ pub struct CommunicationInfo<Id> {
3568
///
3669
/// The execution layer will not call [`finalize`](`Round::finalize`) until all these nodes have responded
3770
/// (and the corresponding [`receive_message`](`Round::receive_message`) finished successfully).
38-
pub expecting_messages_from: BTreeSet<Id>,
71+
pub expecting_messages_from: IdSet<Id>,
3972

4073
/// Returns the specific way the node participates in the echo round following this round.
4174
///
@@ -50,7 +83,7 @@ impl<Id: PartyId> CommunicationInfo<Id> {
5083
pub fn regular(other_parties: &BTreeSet<Id>) -> Self {
5184
Self {
5285
message_destinations: other_parties.clone(),
53-
expecting_messages_from: other_parties.clone(),
86+
expecting_messages_from: IdSet::new_non_threshold(other_parties.clone()),
5487
echo_round_participation: EchoRoundParticipation::Default,
5588
}
5689
}

manul/src/session/session.rs

+15-12
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ use super::{
2424
};
2525
use crate::protocol::{
2626
Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, EchoRoundParticipation,
27-
EntryPoint, FinalizeOutcome, NormalBroadcast, PartyId, Payload, Protocol, ProtocolMessage, ProtocolMessagePart,
28-
ReceiveError, ReceiveErrorType, RoundId, TransitionInfo,
27+
EntryPoint, FinalizeOutcome, IdSet, NormalBroadcast, PartyId, Payload, Protocol, ProtocolMessage,
28+
ProtocolMessagePart, ReceiveError, ReceiveErrorType, RoundId, TransitionInfo,
2929
};
3030

3131
/// A set of types needed to execute a session.
@@ -100,7 +100,7 @@ impl AsRef<[u8]> for SessionId {
100100
#[derive(Debug)]
101101
pub(crate) struct EchoRoundInfo<Verifier> {
102102
pub(crate) message_destinations: BTreeSet<Verifier>,
103-
pub(crate) expecting_messages_from: BTreeSet<Verifier>,
103+
pub(crate) expecting_messages_from: IdSet<Verifier>,
104104
pub(crate) expected_echos: BTreeSet<Verifier>,
105105
}
106106

@@ -180,11 +180,14 @@ where
180180
EchoRoundParticipation::Default => {
181181
if round_sends_echo_broadcast {
182182
// Add our own echo message to the expected list because we expect it to be sent back from other nodes.
183-
let mut expected_echos = communication_info.expecting_messages_from.clone();
183+
let mut expected_echos = communication_info.expecting_messages_from.all().clone();
184184
expected_echos.insert(verifier.clone());
185185
Some(EchoRoundInfo {
186186
message_destinations: communication_info.message_destinations.clone(),
187-
expecting_messages_from: communication_info.message_destinations.clone(),
187+
// TODO: this is not correct in general
188+
expecting_messages_from: IdSet::new_non_threshold(
189+
communication_info.message_destinations.clone(),
190+
),
188191
expected_echos,
189192
})
190193
} else {
@@ -194,8 +197,9 @@ where
194197
EchoRoundParticipation::Send => None,
195198
EchoRoundParticipation::Receive { echo_targets } => Some(EchoRoundInfo {
196199
message_destinations: echo_targets.clone(),
197-
expecting_messages_from: echo_targets.clone(),
198-
expected_echos: communication_info.expecting_messages_from.clone(),
200+
// TODO: this is not correct in general
201+
expecting_messages_from: IdSet::new_non_threshold(echo_targets.clone()),
202+
expected_echos: communication_info.expecting_messages_from.all().clone(),
199203
}),
200204
};
201205

@@ -546,7 +550,7 @@ pub enum CanFinalize {
546550
#[derive_where::derive_where(Debug)]
547551
pub struct RoundAccumulator<P: Protocol<SP::Verifier>, SP: SessionParameters> {
548552
still_have_not_sent_messages: BTreeSet<SP::Verifier>,
549-
expecting_messages_from: BTreeSet<SP::Verifier>,
553+
expecting_messages_from: IdSet<SP::Verifier>,
550554
processing: BTreeSet<SP::Verifier>,
551555
payloads: BTreeMap<SP::Verifier, Payload>,
552556
artifacts: BTreeMap<SP::Verifier, Artifact>,
@@ -563,9 +567,9 @@ where
563567
P: Protocol<SP::Verifier>,
564568
SP: SessionParameters,
565569
{
566-
fn new(expecting_messages_from: &BTreeSet<SP::Verifier>) -> Self {
570+
fn new(expecting_messages_from: &IdSet<SP::Verifier>) -> Self {
567571
Self {
568-
still_have_not_sent_messages: expecting_messages_from.clone(),
572+
still_have_not_sent_messages: expecting_messages_from.all().clone(),
569573
expecting_messages_from: expecting_messages_from.clone(),
570574
processing: BTreeSet::new(),
571575
payloads: BTreeMap::new(),
@@ -582,8 +586,7 @@ where
582586
fn can_finalize(&self) -> CanFinalize {
583587
if self
584588
.expecting_messages_from
585-
.iter()
586-
.all(|key| self.payloads.contains_key(key))
589+
.is_quorum(&self.payloads.keys().cloned().collect::<BTreeSet<_>>())
587590
{
588591
CanFinalize::Yes
589592
} else if !self.still_have_not_sent_messages.is_empty() {

manul/src/tests/partial_echo.rs

+11-10
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use crate::{
1313
dev::{run_sync, BinaryFormat, TestSessionParams, TestSigner, TestVerifier},
1414
protocol::{
1515
Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, EchoRoundParticipation,
16-
EntryPoint, FinalizeOutcome, LocalError, MessageValidationError, NoProtocolErrors, NormalBroadcast, PartyId,
17-
Payload, Protocol, ProtocolMessage, ProtocolMessagePart, ReceiveError, Round, RoundId, TransitionInfo,
16+
EntryPoint, FinalizeOutcome, IdSet, LocalError, MessageValidationError, NoProtocolErrors, NormalBroadcast,
17+
PartyId, Payload, Protocol, ProtocolMessage, ProtocolMessagePart, ReceiveError, Round, RoundId, TransitionInfo,
1818
},
1919
signature::Keypair,
2020
};
@@ -55,7 +55,7 @@ impl<Id: PartyId> Protocol<Id> for PartialEchoProtocol<Id> {
5555
struct Inputs<Id> {
5656
id: Id,
5757
message_destinations: BTreeSet<Id>,
58-
expecting_messages_from: BTreeSet<Id>,
58+
expecting_messages_from: IdSet<Id>,
5959
echo_round_participation: EchoRoundParticipation<Id>,
6060
}
6161

@@ -127,12 +127,13 @@ impl<Id: PartyId + Serialize + for<'de> Deserialize<'de>> Round<Id> for Round1<I
127127
message.normal_broadcast.assert_is_none()?;
128128
message.direct_message.assert_is_none()?;
129129

130-
if self.inputs.expecting_messages_from.is_empty() {
130+
// TODO: or use an Option<>?
131+
if self.inputs.expecting_messages_from.all().is_empty() {
131132
message.echo_broadcast.assert_is_none()?;
132133
} else {
133134
let echo = message.echo_broadcast.deserialize::<Round1Echo<Id>>(format)?;
134135
assert_eq!(&echo.sender, from);
135-
assert!(self.inputs.expecting_messages_from.contains(from));
136+
assert!(self.inputs.expecting_messages_from.all().contains(from));
136137
}
137138

138139
Ok(Payload::new(()))
@@ -163,7 +164,7 @@ fn partial_echo() {
163164
Inputs {
164165
id: signers[0].verifying_key(),
165166
message_destinations: BTreeSet::from([ids[1], ids[2], ids[3]]),
166-
expecting_messages_from: BTreeSet::new(),
167+
expecting_messages_from: IdSet::empty(),
167168
echo_round_participation: EchoRoundParticipation::Send,
168169
},
169170
);
@@ -172,7 +173,7 @@ fn partial_echo() {
172173
Inputs {
173174
id: signers[1].verifying_key(),
174175
message_destinations: BTreeSet::from([ids[2], ids[3]]),
175-
expecting_messages_from: BTreeSet::from([ids[0]]),
176+
expecting_messages_from: IdSet::new_non_threshold([ids[0]].into()),
176177
echo_round_participation: EchoRoundParticipation::Default,
177178
},
178179
);
@@ -181,7 +182,7 @@ fn partial_echo() {
181182
Inputs {
182183
id: signers[2].verifying_key(),
183184
message_destinations: BTreeSet::new(),
184-
expecting_messages_from: BTreeSet::from([ids[0], ids[1]]),
185+
expecting_messages_from: IdSet::new_non_threshold([ids[0], ids[1]].into()),
185186
echo_round_participation: EchoRoundParticipation::Receive {
186187
echo_targets: BTreeSet::from([ids[1], ids[3]]),
187188
},
@@ -192,7 +193,7 @@ fn partial_echo() {
192193
Inputs {
193194
id: signers[3].verifying_key(),
194195
message_destinations: BTreeSet::new(),
195-
expecting_messages_from: BTreeSet::from([ids[0], ids[1]]),
196+
expecting_messages_from: IdSet::new_non_threshold([ids[0], ids[1]].into()),
196197
echo_round_participation: EchoRoundParticipation::Receive {
197198
echo_targets: BTreeSet::from([ids[1], ids[2]]),
198199
},
@@ -203,7 +204,7 @@ fn partial_echo() {
203204
Inputs {
204205
id: signers[4].verifying_key(),
205206
message_destinations: BTreeSet::new(),
206-
expecting_messages_from: BTreeSet::new(),
207+
expecting_messages_from: IdSet::empty(),
207208
echo_round_participation: EchoRoundParticipation::<TestVerifier>::Default,
208209
},
209210
);

0 commit comments

Comments
 (0)