test_semantic/
message.rs

1//! Definitions central to BPCon messages.
2//!
3//! This module defines the structures and functionality for messages used in the BPCon consensus protocol.
4//! It includes message contents, routing information, and utilities for packing and unpacking messages.
5
6use crate::error::{DeserializationError, SerializationError};
7use rkyv::{AlignedVec, Archive, Deserialize, Infallible, Serialize};
8use std::collections::HashSet;
9
10/// A message ready for transfer within the BPCon protocol.
11#[derive(Debug, Clone)]
12pub struct MessagePacket {
13    /// Serialized message contents.
14    pub content_bytes: AlignedVec,
15    /// Routing information for the message.
16    pub routing: MessageRouting,
17}
18
19/// Full routing information for a BPCon message.
20/// This struct is used to manage and route messages through the BPCon protocol.
21#[derive(PartialEq, Eq, Debug, Copy, Clone)]
22pub struct MessageRouting {
23    /// The ID of the participant who sent the message.
24    pub sender: u64,
25    /// The type of BPCon protocol message.
26    pub msg_type: ProtocolMessage,
27}
28
29/// Enumeration of the different types of protocol messages used in BPCon.
30///
31/// These message types represent the various stages of the BPCon consensus protocol,
32/// each corresponding to a specific phase in the process.
33#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash)]
34pub enum ProtocolMessage {
35    Msg1a,
36    Msg1b,
37    Msg2a,
38    Msg2av,
39    Msg2b,
40}
41
42impl std::fmt::Display for ProtocolMessage {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        write!(f, "ProtocolMessage: {:?}", self)
45    }
46}
47
48// The following structures represent the contents of different BPCon protocol messages.
49// Each message type is serialized before being sent, and deserialized upon receipt.
50
51// Value in messages is stored in serialized format, i.e., bytes, to avoid strict restrictions
52// on the `Value` trait being [de]serializable only with `rkyv`.
53
54/// Contents of a BPCon message of type 1a.
55#[derive(Archive, Deserialize, Serialize, Debug, Clone)]
56#[archive(compare(PartialEq), check_bytes)]
57#[archive_attr(derive(Debug))]
58pub struct Message1aContent {
59    /// The ballot number associated with this message.
60    pub ballot: u64,
61}
62
63/// Contents of a BPCon message of type 1b.
64#[derive(Archive, Deserialize, Serialize, Debug, Clone)]
65#[archive(compare(PartialEq), check_bytes)]
66#[archive_attr(derive(Debug))]
67pub struct Message1bContent {
68    /// The ballot number associated with this message.
69    pub ballot: u64,
70    /// The last ballot number that was voted on, if any.
71    pub last_ballot_voted: Option<u64>,
72    /// The last value that was voted on, serialized as a vector of bytes, if any.
73    pub last_value_voted: Option<Vec<u8>>,
74}
75
76/// Contents of a BPCon message of type 2a.
77#[derive(Archive, Deserialize, Serialize, Debug, Clone)]
78#[archive(compare(PartialEq), check_bytes)]
79#[archive_attr(derive(Debug))]
80pub struct Message2aContent {
81    /// The ballot number associated with this message.
82    pub ballot: u64,
83    /// The proposed value, serialized as a vector of bytes.
84    pub value: Vec<u8>,
85}
86
87/// Contents of a BPCon message of type 2av.
88#[derive(Archive, Deserialize, Serialize, Debug, Clone)]
89#[archive(compare(PartialEq), check_bytes)]
90#[archive_attr(derive(Debug))]
91pub struct Message2avContent {
92    /// The ballot number associated with this message.
93    pub ballot: u64,
94    /// The received value, serialized as a vector of bytes.
95    pub received_value: Vec<u8>,
96}
97
98/// Contents of a BPCon message of type 2b.
99#[derive(Archive, Deserialize, Serialize, Debug, Clone)]
100#[archive(compare(PartialEq), check_bytes)]
101#[archive_attr(derive(Debug))]
102pub struct Message2bContent {
103    /// The ballot number associated with this message.
104    pub ballot: u64,
105}
106
107// Macro to implement packing and unpacking logic for each message content type.
108// This logic handles serialization to bytes and deserialization from bytes, as well as setting up routing information.
109
110macro_rules! impl_packable {
111    ($type:ty, $msg_type:expr) => {
112        impl $type {
113            /// Packs the message content into a `MessagePacket` for transfer.
114            ///
115            /// This method serializes the message content and combines it with routing information.
116            ///
117            /// # Parameters
118            ///
119            /// - `sender`: The ID of the participant sending the message.
120            ///
121            /// # Returns
122            ///
123            /// A `MessagePacket` containing the serialized message and routing information, or a `SerializationError` if packing fails.
124            pub fn pack(&self, sender: u64) -> Result<MessagePacket, SerializationError> {
125                let content_bytes = rkyv::to_bytes::<_, 256>(self)
126                    .map_err(|err| SerializationError::Message(err.to_string()))?;
127                Ok(MessagePacket {
128                    content_bytes,
129                    routing: Self::route(sender),
130                })
131            }
132
133            /// Unpacks a `MessagePacket` to extract the original message content.
134            ///
135            /// This method deserializes the message content from the byte representation stored in the packet.
136            ///
137            /// # Parameters
138            ///
139            /// - `msg`: A reference to the `MessagePacket` to be unpacked.
140            ///
141            /// # Returns
142            ///
143            /// The deserialized message content, or a `DeserializationError` if unpacking fails.
144            pub fn unpack(msg: &MessagePacket) -> Result<Self, DeserializationError> {
145                let archived = rkyv::check_archived_root::<Self>(msg.content_bytes.as_slice())
146                    .map_err(|err| DeserializationError::Message(err.to_string()))?;
147                archived
148                    .deserialize(&mut Infallible)
149                    .map_err(|err| DeserializationError::Message(err.to_string()))
150            }
151
152            /// Creates routing information for the message.
153            ///
154            /// This method sets up the routing details, including the sender and the message type.
155            ///
156            /// # Parameters
157            ///
158            /// - `sender`: The ID of the participant sending the message.
159            ///
160            /// # Returns
161            ///
162            /// A `MessageRouting` instance containing the routing information.
163            fn route(sender: u64) -> MessageRouting {
164                MessageRouting {
165                    sender,
166                    msg_type: $msg_type,
167                }
168            }
169        }
170    };
171}
172
173// Implement the packing and unpacking functionality for each message content type.
174impl_packable!(Message1aContent, ProtocolMessage::Msg1a);
175impl_packable!(Message1bContent, ProtocolMessage::Msg1b);
176impl_packable!(Message2aContent, ProtocolMessage::Msg2a);
177impl_packable!(Message2avContent, ProtocolMessage::Msg2av);
178impl_packable!(Message2bContent, ProtocolMessage::Msg2b);
179
180/// A struct to keep track of senders and the cumulative weight of their messages.
181///
182/// `MessageRoundState` is used to monitor the participants who have sent messages and to accumulate
183/// the total weight of these messages. This helps in determining when a quorum has been reached.
184#[derive(PartialEq, Eq, Clone, Debug, Default)]
185pub struct MessageRoundState {
186    senders: HashSet<u64>,
187    weight: u128,
188}
189
190impl MessageRoundState {
191    /// Creates a new, empty `MessageRoundState`.
192    ///
193    /// This method initializes an empty state with no senders and zero weight.
194    ///
195    /// # Returns
196    ///
197    /// A new `MessageRoundState` instance.
198    pub fn new() -> Self {
199        Self::default()
200    }
201
202    /// Returns the cumulative weight of all messages received in this round.
203    ///
204    /// # Returns
205    ///
206    /// The total weight of all messages that have been added to this state.
207    pub fn get_weight(&self) -> u128 {
208        self.weight
209    }
210
211    /// Adds a sender and their corresponding weight to the state.
212    ///
213    /// This method updates the state to include the specified sender and increments the cumulative
214    /// weight by the specified amount.
215    ///
216    /// # Parameters
217    ///
218    /// - `sender`: The ID of the participant sending the message.
219    /// - `weight`: The weight associated with this sender's message.
220    pub fn add_sender(&mut self, sender: u64, weight: u128) {
221        self.senders.insert(sender);
222        self.weight += weight;
223    }
224
225    /// Checks if a sender has already sent a message in this round.
226    ///
227    /// # Parameters
228    ///
229    /// - `sender`: A reference to the ID of the participant.
230    ///
231    /// # Returns
232    ///
233    /// `true` if the sender has already sent a message, `false` otherwise.
234    pub fn contains_sender(&self, sender: &u64) -> bool {
235        self.senders.contains(sender)
236    }
237
238    /// Resets the state for a new round.
239    ///
240    /// This method clears all recorded senders and resets the cumulative weight to zero,
241    /// preparing the state for a new round of message collection.
242    pub fn reset(&mut self) {
243        self.senders.clear();
244        self.weight = 0;
245    }
246}