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}