diff --git a/examples/src/simple.rs b/examples/src/simple.rs index 8517f7b..489ccce 100644 --- a/examples/src/simple.rs +++ b/examples/src/simple.rs @@ -200,7 +200,7 @@ impl Round for Round1 { } fn communication_info(&self) -> CommunicationInfo { - CommunicationInfo::regular(&self.context.other_ids) + CommunicationInfo::all_to_all(&self.context.other_ids) } fn make_normal_broadcast( diff --git a/manul/src/protocol.rs b/manul/src/protocol.rs index 975fc15..4d035d9 100644 --- a/manul/src/protocol.rs +++ b/manul/src/protocol.rs @@ -26,8 +26,8 @@ pub use errors::{ }; pub use message::{DirectMessage, EchoBroadcast, NormalBroadcast, ProtocolMessage, ProtocolMessagePart}; pub use round::{ - Artifact, CommunicationInfo, EchoRoundParticipation, EntryPoint, FinalizeOutcome, NoProtocolErrors, PartyId, - Payload, Protocol, ProtocolError, RequiredMessageParts, RequiredMessages, Round, + Artifact, CommunicationInfo, EntryPoint, FinalizeOutcome, IdSet, NoProtocolErrors, + PartyId, Payload, Protocol, ProtocolError, RequiredMessageParts, RequiredMessages, Round, RoundCommunicationInfo, }; pub use round_id::{RoundId, TransitionInfo}; diff --git a/manul/src/protocol/round.rs b/manul/src/protocol/round.rs index edb21fe..4cd6cf3 100644 --- a/manul/src/protocol/round.rs +++ b/manul/src/protocol/round.rs @@ -19,9 +19,79 @@ use super::{ round_id::{RoundId, TransitionInfo}, }; -/// Describes what other parties this rounds sends messages to, and what other parties it expects messages from. +// TODO: make a trait to implement custom threshold strategies +/// A set of IDs with an associated quorum condition. +#[derive(Debug, Clone)] +pub struct IdSet { + ids: BTreeSet, + threshold: usize, +} + +impl IdSet { + /// Creates a non-threshold ID set (that is, messages from all `ids` must be present for the quorum). + pub fn new_non_threshold(ids: BTreeSet) -> Self { + let threshold = ids.len(); + Self { ids, threshold } + } + + /// Creates an empty ID set. + pub fn empty() -> Self { + Self { + ids: BTreeSet::new(), + threshold: 0, + } + } + + pub(crate) fn all(&self) -> &BTreeSet { + &self.ids + } + + pub(crate) fn is_quorum(&self, ids: &BTreeSet) -> bool { + // TODO: assuming `ids` are a subset of `self.ids`. Can we? + ids.len() >= self.threshold + } + + pub(crate) fn is_quorum_possible(&self, banned_ids: &BTreeSet) -> bool { + let ids = self.ids.intersection(banned_ids).collect::>(); + self.ids.len() - ids.len() >= self.threshold + } +} + +/// Encapsulates the communication info for the main round and the possible echo round. #[derive(Debug, Clone)] pub struct CommunicationInfo { + /// Communication info for the main part of the round (that is, not considering the echo round). + pub main_round: RoundCommunicationInfo, + + /// The specific way the node participates in the echo round following this round. + /// + /// If `None`, and there is an echo round, will be set to `main_round`. + pub echo_round: Option>, + + /// The parties whose echoed messages this node expects to receive in the echo round (if any). + /// + /// If `None`, and there is an echo round, will be set to `echo_round.expecting_messages_from`. + pub expected_echos: Option>, +} + +impl CommunicationInfo { + /// A regular round that sends messages to all `other_parties`, and expects messages back from them. + pub fn all_to_all(id_set: &IdSet) -> Self { + Self { + main_round: RoundCommunicationInfo::all_to_all(id_set), + echo_round: None, + expected_echos: None, + } + } + + pub fn set_to_set(senders: &IdSet, receivers: &IdSet, my_id: &Id) -> Self { + todo!() + } +} + +/// Describes what other parties this rounds sends messages to, and what other parties it expects messages from. +#[derive(Debug, Clone)] +pub struct RoundCommunicationInfo { /// The destinations of the messages to be sent out by this round. /// /// The way it is interpreted by the execution layer is @@ -31,27 +101,26 @@ pub struct CommunicationInfo { /// for each element of the returned set. pub message_destinations: BTreeSet, - /// Returns the set of node IDs from which this round expects messages. + /// The set of node IDs from which this round expects messages. /// - /// The execution layer will not call [`finalize`](`Round::finalize`) until all these nodes have responded - /// (and the corresponding [`receive_message`](`Round::receive_message`) finished successfully). - pub expecting_messages_from: BTreeSet, - - /// Returns the specific way the node participates in the echo round following this round. - /// - /// Returns [`EchoRoundParticipation::Default`] by default; this works fine when every node - /// sends messages to every other one, or do not send or receive any echo broadcasts. - /// Otherwise, review the options in [`EchoRoundParticipation`] and pick the appropriate one. - pub echo_round_participation: EchoRoundParticipation, + /// The execution layer will not call [`finalize`](`Round::finalize`) until enough nodes to constitute the quorum + /// have responded (and the corresponding [`receive_message`](`Round::receive_message`) finished successfully). + pub expecting_messages_from: IdSet, } -impl CommunicationInfo { +impl RoundCommunicationInfo { /// A regular round that sends messages to all `other_parties`, and expects messages back from them. - pub fn regular(other_parties: &BTreeSet) -> Self { + pub fn all_to_all(id_set: &IdSet) -> Self { Self { - message_destinations: other_parties.clone(), - expecting_messages_from: other_parties.clone(), - echo_round_participation: EchoRoundParticipation::Default, + message_destinations: id_set.all().clone(), + expecting_messages_from: id_set.clone(), + } + } + + pub fn none() -> Self { + Self { + message_destinations: BTreeSet::new(), + expecting_messages_from: IdSet::empty(), } } } @@ -338,25 +407,6 @@ pub trait PartyId: 'static + Debug + Clone + Ord + Send + Sync + Serialize + for impl PartyId for T where T: 'static + Debug + Clone + Ord + Send + Sync + Serialize + for<'de> Deserialize<'de> {} -/// The specific way the node participates in the echo round (if any). -#[derive(Debug, Clone)] -pub enum EchoRoundParticipation { - /// The default behavior: sends broadcasts and receives echoed messages, or does neither. - /// - /// That is, this node will be a part of the echo round if [`Round::make_echo_broadcast`] generates a message. - Default, - - /// This node sends broadcasts that will be echoed, but does not receive any. - Send, - - /// This node receives broadcasts that it needs to echo, but does not send any itself. - Receive { - /// The other participants of the echo round - /// (that is, the nodes to which echoed messages will be sent). - echo_targets: BTreeSet, - }, -} - mod sealed { /// A dyn safe trait to get the type's ID. pub trait DynTypeId: 'static { diff --git a/manul/src/session/echo.rs b/manul/src/session/echo.rs index 5930bd4..b902626 100644 --- a/manul/src/session/echo.rs +++ b/manul/src/session/echo.rs @@ -13,12 +13,12 @@ use tracing::debug; use super::{ message::{MessageVerificationError, SignedMessageHash, SignedMessagePart}, - session::{EchoRoundInfo, SessionParameters}, + session::{SessionParameters}, LocalError, }; use crate::{ - protocol::{ - Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, EchoRoundParticipation, + protocol::{RoundCommunicationInfo, IdSet, + Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, FinalizeOutcome, MessageValidationError, NormalBroadcast, Payload, Protocol, ProtocolMessage, ProtocolMessagePart, ReceiveError, Round, TransitionInfo, }, @@ -34,7 +34,12 @@ pub(crate) enum EchoRoundError { /// /// The attached identifier points out the sender for whom the echoed message was invalid, /// to speed up the verification process. - InvalidEcho(Id), + InvalidEcho { + // Even though this will be the same as the message sender, it is convenient to record it here + // because of the way this error will be processed. + guilty_party: Id, + failed_for: Id, + }, /// The originally received message and the one received in the echo pack were both valid, /// but different. /// @@ -49,7 +54,7 @@ pub(crate) enum EchoRoundError { impl EchoRoundError { pub(crate) fn description(&self) -> String { match self { - Self::InvalidEcho(_) => "Invalid message received among the ones echoed".into(), + Self::InvalidEcho { .. } => "Invalid message received among the ones echoed".into(), Self::MismatchedBroadcasts { .. } => { "The echoed message is different from the originally received one".into() } @@ -70,8 +75,9 @@ pub(crate) struct EchoRoundMessage { pub struct EchoRound, SP: SessionParameters> { verifier: SP::Verifier, echo_broadcasts: BTreeMap>, - echo_round_info: EchoRoundInfo, communication_info: CommunicationInfo, + expected_echos: IdSet, + banned_ids: BTreeSet, main_round: BoxedRound, payloads: BTreeMap, artifacts: BTreeMap, @@ -85,24 +91,25 @@ where pub fn new( verifier: SP::Verifier, echo_broadcasts: BTreeMap>, - echo_round_info: EchoRoundInfo, + communication_info: RoundCommunicationInfo, + expected_echos: IdSet, + banned_ids: BTreeSet, main_round: BoxedRound, payloads: BTreeMap, artifacts: BTreeMap, ) -> Self { - debug!("{:?}: initialized echo round with {:?}", verifier, echo_round_info); - + debug!("{:?}: initialized echo round with {:?} {:?}", verifier, communication_info, expected_echos); let communication_info = CommunicationInfo { - message_destinations: echo_round_info.message_destinations.clone(), - expecting_messages_from: echo_round_info.expecting_messages_from.clone(), - echo_round_participation: EchoRoundParticipation::Default, + main_round: communication_info, + echo_round: None, + expected_echos: None, }; - Self { verifier, echo_broadcasts, - echo_round_info, communication_info, + expected_echos, + banned_ids, main_round, payloads, artifacts, @@ -192,23 +199,16 @@ where let message = message.normal_broadcast.deserialize::>(format)?; // Check that the received message contains entries from `expected_echos`. - // It is an unprovable fault. + // Since we cannot guarantee the communication info for the echo round is in the associated data, + // we cannot construct an evidence for this fault. - let mut expected_keys = self.echo_round_info.expected_echos.clone(); + let mut expected_keys = self.expected_echos.all().clone(); // We don't expect the node to send its echo the second time. expected_keys.remove(from); let message_keys = message.message_hashes.keys().cloned().collect::>(); - let missing_keys = expected_keys.difference(&message_keys).collect::>(); - if !missing_keys.is_empty() { - return Err(ReceiveError::unprovable(format!( - "Missing echoed messages from: {:?}", - missing_keys - ))); - } - let extra_keys = message_keys.difference(&expected_keys).collect::>(); if !extra_keys.is_empty() { return Err(ReceiveError::unprovable(format!( @@ -217,6 +217,14 @@ where ))); } + // Check that the echos we received, minus the banned IDs, constitute a quorum. + // This is also unprovable since the information about the IDs we banned is not available to third parties. + + let expected_keys = message_keys.difference(&self.banned_ids).cloned().collect::>(); + if !self.expected_echos.is_quorum(&expected_keys) { + return Err(ReceiveError::unprovable("Not enough echos to constitute a quorum")); + } + // Check that every entry is equal to what we received previously (in the main round). // If there's a difference, it's a provable fault, // since we have both messages signed by `from`. @@ -236,17 +244,29 @@ where // This means `from` sent us an incorrectly signed message. // Provable fault of `from`. Err(MessageVerificationError::InvalidSignature) => { - return Err(EchoRoundError::InvalidEcho(sender.clone()).into()) + return Err(EchoRoundError::InvalidEcho { + guilty_party: from.clone(), + failed_for: sender.clone(), + } + .into()) } Err(MessageVerificationError::SignatureMismatch) => { - return Err(EchoRoundError::InvalidEcho(sender.clone()).into()) + return Err(EchoRoundError::InvalidEcho { + guilty_party: from.clone(), + failed_for: sender.clone(), + } + .into()) } }; // `from` sent us a correctly signed message but from another round or another session. // Provable fault of `from`. if verified_echo.metadata() != previously_received_echo.metadata() { - return Err(EchoRoundError::InvalidEcho(sender.clone()).into()); + return Err(EchoRoundError::InvalidEcho { + guilty_party: from.clone(), + failed_for: sender.clone(), + } + .into()); } // `sender` sent us and `from` messages with different payloads, diff --git a/manul/src/session/evidence.rs b/manul/src/session/evidence.rs index 5bfd148..ab0677a 100644 --- a/manul/src/session/evidence.rs +++ b/manul/src/session/evidence.rs @@ -164,18 +164,20 @@ where } pub(crate) fn new_echo_round_error( - verifier: &SP::Verifier, normal_broadcast: SignedMessagePart, error: EchoRoundError, ) -> Result { let description = format!("Echo round error: {}", error.description()); match error { - EchoRoundError::InvalidEcho(from) => Ok(Self { - guilty_party: verifier.clone(), + EchoRoundError::InvalidEcho { + guilty_party, + failed_for, + } => Ok(Self { + guilty_party, description, evidence: EvidenceEnum::InvalidEchoPack(InvalidEchoPackEvidence { normal_broadcast, - invalid_echo_sender: from, + invalid_echo_sender: failed_for, }), }), EchoRoundError::MismatchedBroadcasts { diff --git a/manul/src/session/session.rs b/manul/src/session/session.rs index 1f0ec2c..6f60636 100644 --- a/manul/src/session/session.rs +++ b/manul/src/session/session.rs @@ -23,9 +23,9 @@ use super::{ LocalError, RemoteError, }; use crate::protocol::{ - Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, EchoRoundParticipation, - EntryPoint, FinalizeOutcome, NormalBroadcast, PartyId, Payload, Protocol, ProtocolMessage, ProtocolMessagePart, - ReceiveError, ReceiveErrorType, RoundId, TransitionInfo, + Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, + EntryPoint, FinalizeOutcome, IdSet, NormalBroadcast, PartyId, Payload, Protocol, ProtocolMessage, + ProtocolMessagePart, ReceiveError, ReceiveErrorType, RoundCommunicationInfo, RoundId, TransitionInfo, }; /// A set of types needed to execute a session. @@ -97,13 +97,6 @@ impl AsRef<[u8]> for SessionId { } } -#[derive(Debug)] -pub(crate) struct EchoRoundInfo { - pub(crate) message_destinations: BTreeSet, - pub(crate) expecting_messages_from: BTreeSet, - pub(crate) expected_echos: BTreeSet, -} - /// An object encapsulating the currently active round, transport protocol, /// and the database of messages and errors from the previous rounds. #[derive(Debug)] @@ -113,8 +106,10 @@ pub struct Session, SP: SessionParameters> { verifier: SP::Verifier, format: BoxedFormat, round: BoxedRound, - communication_info: CommunicationInfo, - echo_round_info: Option>, + message_destinations: BTreeSet, + main_round_cinfo: RoundCommunicationInfo, + echo_round_cinfo: RoundCommunicationInfo, + expected_echos: IdSet, echo_broadcast: SignedMessagePart, normal_broadcast: SignedMessagePart, transition_info: TransitionInfo, @@ -173,31 +168,20 @@ where let normal = round.as_ref().make_normal_broadcast(rng, &format)?; let normal_broadcast = SignedMessagePart::new::(rng, &signer, &session_id, &transition_info.id(), normal)?; - let communication_info = round.as_ref().communication_info(); - - let round_sends_echo_broadcast = !echo_broadcast.payload().is_none(); - let echo_round_info = match &communication_info.echo_round_participation { - EchoRoundParticipation::Default => { - if round_sends_echo_broadcast { - // Add our own echo message to the expected list because we expect it to be sent back from other nodes. - let mut expected_echos = communication_info.expecting_messages_from.clone(); - expected_echos.insert(verifier.clone()); - Some(EchoRoundInfo { - message_destinations: communication_info.message_destinations.clone(), - expecting_messages_from: communication_info.message_destinations.clone(), - expected_echos, - }) - } else { - None - } - } - EchoRoundParticipation::Send => None, - EchoRoundParticipation::Receive { echo_targets } => Some(EchoRoundInfo { - message_destinations: echo_targets.clone(), - expecting_messages_from: echo_targets.clone(), - expected_echos: communication_info.expecting_messages_from.clone(), - }), - }; + let CommunicationInfo { + main_round: main_round_cinfo, + echo_round: echo_round_cinfo, + expected_echos, + } = round.as_ref().communication_info(); + + let echo_round_cinfo = echo_round_cinfo.unwrap_or(main_round_cinfo.clone()); + let expected_echos = expected_echos.unwrap_or(echo_round_cinfo.expecting_messages_from.clone()); + + let message_destinations = main_round_cinfo + .message_destinations + .difference(&transcript.banned_ids()) + .cloned() + .collect::>(); Ok(Self { session_id, @@ -208,8 +192,10 @@ where echo_broadcast, normal_broadcast, transition_info, - communication_info, - echo_round_info, + message_destinations, + main_round_cinfo, + echo_round_cinfo, + expected_echos, transcript, }) } @@ -226,7 +212,7 @@ where /// Returns the set of message destinations for the current round. pub fn message_destinations(&self) -> &BTreeSet { - &self.communication_info.message_destinations + &self.message_destinations } /// Creates the message to be sent to the given destination. @@ -237,6 +223,12 @@ where rng: &mut impl CryptoRngCore, destination: &SP::Verifier, ) -> Result<(Message, ProcessedArtifact), LocalError> { + if !self.message_destinations.contains(destination) { + return Err(LocalError::new( + "Destination {destination} is not in the set of message destinations for this round", + )); + } + let (direct_message, artifact) = self .round .as_ref() @@ -296,7 +288,7 @@ where ) -> Result, LocalError> { // Quick preliminary checks, before we proceed with more expensive verification let key = self.verifier(); - if self.transcript.is_banned(from) || accum.is_banned(from) { + if accum.is_banned(from) { trace!("{key:?} Banned."); return Ok(PreprocessOutcome::remote_error("The sender is banned")); } @@ -326,7 +318,7 @@ where let acceptable_round_ids = self .transition_info - .simultaneous_rounds(self.echo_round_info.is_some())?; + .simultaneous_rounds(self.echo_round_cinfo.is_some())?; let message_for = if message_round_id == self.round_id() { if accum.message_is_being_processed(from) { @@ -413,7 +405,10 @@ where /// Makes an accumulator for a new round. pub fn make_accumulator(&self) -> RoundAccumulator { - RoundAccumulator::new(&self.communication_info.expecting_messages_from) + RoundAccumulator::new( + &self.main_round_cinfo.expecting_messages_from, + self.transcript.banned_ids(), + ) } fn terminate_inner( @@ -464,6 +459,8 @@ where let verifier = self.verifier().clone(); let round_id = self.round_id(); + let echo_round = !self.echo_broadcast.payload().is_none(); + let transcript = self.transcript.update( &round_id, (verifier.clone(), self.echo_broadcast), @@ -475,11 +472,13 @@ where accum.still_have_not_sent_messages, )?; - if let Some(echo_round_info) = self.echo_round_info { + if echo_round { let round = BoxedRound::new_dynamic(EchoRound::::new( verifier, transcript.echo_broadcasts(&round_id)?, - echo_round_info, + self.echo_round_cinfo, + self.expected_echos, + transcript.banned_ids().clone(), self.round, accum.payloads, accum.artifacts, @@ -545,8 +544,9 @@ pub enum CanFinalize { /// A mutable accumulator for collecting the results and errors from processing messages for a single round. #[derive_where::derive_where(Debug)] pub struct RoundAccumulator, SP: SessionParameters> { + banned_ids: BTreeSet, still_have_not_sent_messages: BTreeSet, - expecting_messages_from: BTreeSet, + expecting_messages_from: IdSet, processing: BTreeSet, payloads: BTreeMap, artifacts: BTreeMap, @@ -563,9 +563,10 @@ where P: Protocol, SP: SessionParameters, { - fn new(expecting_messages_from: &BTreeSet) -> Self { + fn new(expecting_messages_from: &IdSet, banned_ids: BTreeSet) -> Self { Self { - still_have_not_sent_messages: expecting_messages_from.clone(), + banned_ids, + still_have_not_sent_messages: expecting_messages_from.all().clone(), expecting_messages_from: expecting_messages_from.clone(), processing: BTreeSet::new(), payloads: BTreeMap::new(), @@ -582,11 +583,10 @@ where fn can_finalize(&self) -> CanFinalize { if self .expecting_messages_from - .iter() - .all(|key| self.payloads.contains_key(key)) + .is_quorum(&self.payloads.keys().cloned().collect::>()) { CanFinalize::Yes - } else if !self.still_have_not_sent_messages.is_empty() { + } else if self.expecting_messages_from.is_quorum_possible(&self.banned_ids) { CanFinalize::NotYet } else { CanFinalize::Never @@ -594,7 +594,7 @@ where } fn is_banned(&self, from: &SP::Verifier) -> bool { - self.provable_errors.contains_key(from) || self.unprovable_errors.contains_key(from) + self.banned_ids.contains(from) } fn message_is_being_processed(&self, from: &SP::Verifier) -> bool { @@ -616,6 +616,7 @@ where from ))) } else { + self.banned_ids.insert(from.clone()); Ok(()) } } @@ -627,6 +628,7 @@ where from ))) } else { + self.banned_ids.insert(from.clone()); Ok(()) } } @@ -725,13 +727,10 @@ where )?; self.register_provable_error(&from, evidence) } - ReceiveErrorType::Unprovable(error) => { - self.unprovable_errors.insert(from.clone(), error); - Ok(()) - } + ReceiveErrorType::Unprovable(error) => self.register_unprovable_error(&from, error), ReceiveErrorType::Echo(error) => { let (_echo_broadcast, normal_broadcast, _direct_message) = processed.message.into_parts(); - let evidence = Evidence::new_echo_round_error(&from, normal_broadcast, *error)?; + let evidence = Evidence::new_echo_round_error(normal_broadcast, *error)?; self.register_provable_error(&from, evidence) } ReceiveErrorType::Local(error) => Err(error), diff --git a/manul/src/session/transcript.rs b/manul/src/session/transcript.rs index 265d44b..1f03f5c 100644 --- a/manul/src/session/transcript.rs +++ b/manul/src/session/transcript.rs @@ -163,6 +163,12 @@ where .ok_or_else(|| LocalError::new(format!("No direct messages registered for {from:?} in {round_id:?}"))) } + pub fn banned_ids(&self) -> BTreeSet { + let mut banned = self.provable_errors.keys().cloned().collect::>(); + banned.extend(self.unprovable_errors.keys().cloned()); + banned + } + pub fn is_banned(&self, from: &SP::Verifier) -> bool { self.provable_errors.contains_key(from) || self.unprovable_errors.contains_key(from) } diff --git a/manul/src/tests/partial_echo.rs b/manul/src/tests/partial_echo.rs index 82e1bb5..5102be0 100644 --- a/manul/src/tests/partial_echo.rs +++ b/manul/src/tests/partial_echo.rs @@ -13,8 +13,8 @@ use crate::{ dev::{run_sync, BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, protocol::{ Artifact, BoxedFormat, BoxedRound, CommunicationInfo, DirectMessage, EchoBroadcast, EchoRoundParticipation, - EntryPoint, FinalizeOutcome, LocalError, MessageValidationError, NoProtocolErrors, NormalBroadcast, PartyId, - Payload, Protocol, ProtocolMessage, ProtocolMessagePart, ReceiveError, Round, RoundId, TransitionInfo, + EntryPoint, FinalizeOutcome, IdSet, LocalError, MessageValidationError, NoProtocolErrors, NormalBroadcast, + PartyId, Payload, Protocol, ProtocolMessage, ProtocolMessagePart, ReceiveError, Round, RoundId, TransitionInfo, }, signature::Keypair, }; @@ -55,7 +55,7 @@ impl Protocol for PartialEchoProtocol { struct Inputs { id: Id, message_destinations: BTreeSet, - expecting_messages_from: BTreeSet, + expecting_messages_from: IdSet, echo_round_participation: EchoRoundParticipation, } @@ -127,12 +127,13 @@ impl Deserialize<'de>> Round for Round1? + if self.inputs.expecting_messages_from.all().is_empty() { message.echo_broadcast.assert_is_none()?; } else { let echo = message.echo_broadcast.deserialize::>(format)?; assert_eq!(&echo.sender, from); - assert!(self.inputs.expecting_messages_from.contains(from)); + assert!(self.inputs.expecting_messages_from.all().contains(from)); } Ok(Payload::new(())) @@ -163,7 +164,7 @@ fn partial_echo() { Inputs { id: signers[0].verifying_key(), message_destinations: BTreeSet::from([ids[1], ids[2], ids[3]]), - expecting_messages_from: BTreeSet::new(), + expecting_messages_from: IdSet::empty(), echo_round_participation: EchoRoundParticipation::Send, }, ); @@ -172,7 +173,7 @@ fn partial_echo() { Inputs { id: signers[1].verifying_key(), message_destinations: BTreeSet::from([ids[2], ids[3]]), - expecting_messages_from: BTreeSet::from([ids[0]]), + expecting_messages_from: IdSet::new_non_threshold([ids[0]].into()), echo_round_participation: EchoRoundParticipation::Default, }, ); @@ -181,7 +182,7 @@ fn partial_echo() { Inputs { id: signers[2].verifying_key(), message_destinations: BTreeSet::new(), - expecting_messages_from: BTreeSet::from([ids[0], ids[1]]), + expecting_messages_from: IdSet::new_non_threshold([ids[0], ids[1]].into()), echo_round_participation: EchoRoundParticipation::Receive { echo_targets: BTreeSet::from([ids[1], ids[3]]), }, @@ -192,7 +193,7 @@ fn partial_echo() { Inputs { id: signers[3].verifying_key(), message_destinations: BTreeSet::new(), - expecting_messages_from: BTreeSet::from([ids[0], ids[1]]), + expecting_messages_from: IdSet::new_non_threshold([ids[0], ids[1]].into()), echo_round_participation: EchoRoundParticipation::Receive { echo_targets: BTreeSet::from([ids[1], ids[2]]), }, @@ -203,7 +204,7 @@ fn partial_echo() { Inputs { id: signers[4].verifying_key(), message_destinations: BTreeSet::new(), - expecting_messages_from: BTreeSet::new(), + expecting_messages_from: IdSet::empty(), echo_round_participation: EchoRoundParticipation::::Default, }, );