1use std::marker::PhantomData;
2
3use committable::{Commitment, Committable};
4use hotshot_types::{
5 data::{
6 EpochNumber, VidDisperseShare2, ViewNumber, vid_disperse::AvidmGf2DisperseShareFragment,
7 },
8 message::Proposal as SignedProposal,
9 request_response::ProposalRequestPayload,
10 simple_certificate::{
11 OneHonestThreshold, SimpleCertificate, SuccessThreshold, TimeoutCertificate2,
12 },
13 simple_vote::{
14 HasEpoch, LightClientStateUpdateVote2, QuorumVote2, SimpleVote, TimeoutData2, TimeoutVote2,
15 Vote2Data,
16 },
17 traits::{node_implementation::NodeType, signature_key::SignatureKey},
18 vote::HasViewNumber,
19};
20pub use hotshot_types::{
21 new_protocol::Proposal,
22 simple_certificate::{Certificate1, Certificate2},
23};
24use serde::{Deserialize, Serialize};
25
26pub type Vote2<T> = SimpleVote<T, Vote2Data<T>>;
27pub type TimeoutCertificate<T> = SimpleCertificate<T, TimeoutData2, SuccessThreshold>;
28pub type TimeoutOneHonest<T> = SimpleCertificate<T, TimeoutData2, OneHonestThreshold>;
29
30#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Deserialize)]
31pub enum Unchecked {}
32
33#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize)]
34pub enum Validated {}
35
36#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
37#[serde(bound(deserialize = "S: Deserialize<'de>"))]
38pub struct ProposalMessage<T: NodeType, S> {
39 pub proposal: SignedProposal<T, Proposal<T>>,
40 #[serde(skip)]
41 _marker: PhantomData<fn() -> S>,
42}
43
44impl<T: NodeType> ProposalMessage<T, Validated> {
45 pub fn validated(p: SignedProposal<T, Proposal<T>>) -> Self {
46 Self {
47 proposal: p,
48 _marker: PhantomData,
49 }
50 }
51}
52
53impl<T: NodeType> ProposalMessage<T, Unchecked> {
54 pub fn unchecked(p: SignedProposal<T, Proposal<T>>) -> Self {
56 Self {
57 proposal: p,
58 _marker: PhantomData,
59 }
60 }
61}
62
63impl<T: NodeType, S> ProposalMessage<T, S> {
64 #[cfg(test)]
65 pub fn into_unchecked(self) -> ProposalMessage<T, Unchecked> {
66 ProposalMessage {
67 proposal: self.proposal,
68 _marker: PhantomData,
69 }
70 }
71}
72
73impl<T: NodeType, S> HasViewNumber for ProposalMessage<T, S> {
74 fn view_number(&self) -> ViewNumber {
75 self.proposal.data.view_number
76 }
77}
78
79pub type VidShareMessage<T> = SignedProposal<T, VidDisperseShare2<T>>;
81
82pub type VidShareFragmentMessage<T> = SignedProposal<T, AvidmGf2DisperseShareFragment<T>>;
87
88#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
89#[serde(bound(deserialize = ""))]
90pub struct Vote1<T: NodeType> {
91 pub vote: QuorumVote2<T>,
92 pub state_vote: Option<LightClientStateUpdateVote2<T>>,
94}
95
96impl<T: NodeType> HasViewNumber for Vote1<T> {
97 fn view_number(&self) -> ViewNumber {
98 self.vote.view_number()
99 }
100}
101
102#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
103#[serde(bound(deserialize = ""))]
104pub struct TimeoutVoteMessage<T: NodeType> {
105 pub vote: TimeoutVote2<T>,
106 pub evidence: Option<CatchupEvidence<T>>,
107}
108
109impl<T: NodeType> HasViewNumber for TimeoutVoteMessage<T> {
110 fn view_number(&self) -> ViewNumber {
111 self.vote.view_number()
112 }
113}
114
115#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
120#[serde(bound(deserialize = ""))]
121pub enum CatchupEvidence<T: NodeType> {
122 Qc(Certificate1<T>),
123 Tc(TimeoutCertificate2<T>),
124}
125
126impl<T: NodeType> HasViewNumber for CatchupEvidence<T> {
127 fn view_number(&self) -> ViewNumber {
128 match self {
129 Self::Qc(qc) => qc.view_number(),
130 Self::Tc(tc) => tc.view_number(),
131 }
132 }
133}
134
135#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
146#[serde(bound(deserialize = "S: Deserialize<'de>"))]
147pub struct EpochChangeMessage<T: NodeType, S> {
148 pub cert1: Certificate1<T>,
149 pub cert2: Certificate2<T>,
150 pub proposal: Proposal<T>,
151 #[serde(skip)]
152 _marker: PhantomData<fn() -> S>,
153}
154
155impl<T: NodeType> EpochChangeMessage<T, Validated> {
156 pub fn validated(
158 cert1: Certificate1<T>,
159 cert2: Certificate2<T>,
160 proposal: Proposal<T>,
161 ) -> Self {
162 Self {
163 cert1,
164 cert2,
165 proposal,
166 _marker: PhantomData,
167 }
168 }
169}
170
171impl<T: NodeType> EpochChangeMessage<T, Unchecked> {
172 pub(crate) fn into_validated(self) -> EpochChangeMessage<T, Validated> {
174 EpochChangeMessage {
175 cert1: self.cert1,
176 cert2: self.cert2,
177 proposal: self.proposal,
178 _marker: PhantomData,
179 }
180 }
181}
182
183impl<T: NodeType, S> EpochChangeMessage<T, S> {
184 #[cfg(test)]
185 pub fn into_unchecked(self) -> EpochChangeMessage<T, Unchecked> {
186 EpochChangeMessage {
187 cert1: self.cert1,
188 cert2: self.cert2,
189 proposal: self.proposal,
190 _marker: PhantomData,
191 }
192 }
193}
194
195impl<T: NodeType, S> HasViewNumber for EpochChangeMessage<T, S> {
196 fn view_number(&self) -> ViewNumber {
197 self.cert1.view_number()
198 }
199}
200
201impl<T: NodeType, S> HasEpoch for EpochChangeMessage<T, S> {
202 fn epoch(&self) -> Option<EpochNumber> {
203 self.cert1.epoch()
204 }
205}
206
207#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
208#[serde(bound(deserialize = ""))]
209pub struct ProposalFetchRequest<T: NodeType> {
210 pub payload: ProposalRequestPayload<T>,
211 pub signature: <T::SignatureKey as SignatureKey>::PureAssembledSignatureType,
212}
213
214impl<T: NodeType> ProposalFetchRequest<T> {
215 pub fn new(
216 view_number: ViewNumber,
217 key: T::SignatureKey,
218 private_key: &<T::SignatureKey as SignatureKey>::PrivateKey,
219 ) -> Result<Self, <T::SignatureKey as SignatureKey>::SignError> {
220 let payload = ProposalRequestPayload { view_number, key };
221 let signature = T::SignatureKey::sign(private_key, payload.commit().as_ref())?;
222 Ok(Self { payload, signature })
223 }
224
225 pub fn validate_sender(&self, sender: &T::SignatureKey) -> bool {
226 &self.payload.key == sender
227 && self
228 .payload
229 .key
230 .validate(&self.signature, self.payload.commit().as_ref())
231 }
232}
233
234impl<T: NodeType> HasViewNumber for ProposalFetchRequest<T> {
235 fn view_number(&self) -> ViewNumber {
236 self.payload.view_number
237 }
238}
239
240#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
241#[serde(bound(deserialize = "S: Deserialize<'de>"))]
242#[allow(clippy::large_enum_variant)]
243pub enum ConsensusMessage<T: NodeType, S> {
244 Proposal(ProposalMessage<T, S>),
245 Vote1(Vote1<T>),
246 Vote2(Vote2<T>),
247 Certificate1(Certificate1<T>, T::SignatureKey),
248 Certificate2(Certificate2<T>, T::SignatureKey),
249 TimeoutVote(TimeoutVoteMessage<T>),
250 TimeoutCertificate(TimeoutCertificate2<T>),
251 EpochChange(EpochChangeMessage<T, S>),
252 VidShareFragment(VidShareFragmentMessage<T>),
254 VidShareBroadcast(VidDisperseShare2<T>),
256 HighQc(Certificate1<T>),
257}
258
259impl<T: NodeType, S> ConsensusMessage<T, S> {
260 #[cfg(test)]
261 pub fn into_unchecked(self) -> ConsensusMessage<T, Unchecked> {
262 match self {
263 Self::Proposal(p) => ConsensusMessage::Proposal(p.into_unchecked()),
264 Self::Vote1(v) => ConsensusMessage::Vote1(v),
265 Self::Vote2(v) => ConsensusMessage::Vote2(v),
266 Self::Certificate1(c, k) => ConsensusMessage::Certificate1(c, k),
267 Self::Certificate2(c, k) => ConsensusMessage::Certificate2(c, k),
268 Self::TimeoutVote(v) => ConsensusMessage::TimeoutVote(v),
269 Self::TimeoutCertificate(c) => ConsensusMessage::TimeoutCertificate(c),
270 Self::EpochChange(c) => ConsensusMessage::EpochChange(c.into_unchecked()),
271 Self::VidShareFragment(v) => ConsensusMessage::VidShareFragment(v),
272 Self::VidShareBroadcast(v) => ConsensusMessage::VidShareBroadcast(v),
273 Self::HighQc(c) => ConsensusMessage::HighQc(c),
274 }
275 }
276}
277
278impl<T: NodeType, S> HasViewNumber for ConsensusMessage<T, S> {
279 fn view_number(&self) -> ViewNumber {
280 match self {
281 Self::Proposal(proposal) => proposal.view_number(),
282 Self::Vote1(vote) => vote.view_number(),
283 Self::Vote2(vote) => vote.view_number(),
284 Self::Certificate1(certificate, _) => certificate.view_number(),
285 Self::Certificate2(certificate, _) => certificate.view_number(),
286 Self::TimeoutVote(msg) => msg.view_number(),
287 Self::TimeoutCertificate(certificate) => certificate.view_number(),
288 Self::EpochChange(epoch_change) => epoch_change.cert1.view_number(),
289 Self::VidShareFragment(fragment) => fragment.data.view_number(),
290 Self::VidShareBroadcast(vid_share) => vid_share.view_number(),
291 Self::HighQc(certificate) => certificate.view_number(),
292 }
293 }
294}
295
296#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
297#[serde(bound(deserialize = ""))]
298pub enum ProposalFetchMessage<T: NodeType> {
299 Request(ProposalFetchRequest<T>),
300 Response(Box<SignedProposal<T, Proposal<T>>>),
301}
302
303impl<T: NodeType> HasViewNumber for ProposalFetchMessage<T> {
304 fn view_number(&self) -> ViewNumber {
305 match self {
306 Self::Request(request) => request.view_number(),
307 Self::Response(proposal) => proposal.data.view_number(),
308 }
309 }
310}
311
312#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
313#[serde(bound(deserialize = ""))]
314pub struct DedupManifest<T: NodeType> {
315 pub(crate) view: ViewNumber,
316 pub(crate) epoch: EpochNumber,
317 pub(crate) hashes: Vec<Commitment<T::Transaction>>,
318}
319
320#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
321#[serde(bound(deserialize = ""))]
322pub struct TransactionMessage<T: NodeType> {
323 pub(crate) view: ViewNumber,
324 pub(crate) transactions: Vec<T::Transaction>,
325}
326
327#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
328#[serde(bound(deserialize = ""))]
329pub enum BlockMessage<T: NodeType> {
330 Transactions(TransactionMessage<T>),
331 DedupManifest(DedupManifest<T>),
332}
333
334impl<T: NodeType> HasViewNumber for BlockMessage<T> {
335 fn view_number(&self) -> ViewNumber {
336 match self {
337 BlockMessage::Transactions(msg) => msg.view,
338 BlockMessage::DedupManifest(msg) => msg.view,
339 }
340 }
341}
342
343#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
344#[serde(bound(deserialize = "S: Deserialize<'de>"))]
345#[allow(clippy::large_enum_variant)]
346pub enum MessageType<T: NodeType, S> {
347 Consensus(ConsensusMessage<T, S>),
348 Block(BlockMessage<T>),
349 ProposalFetch(ProposalFetchMessage<T>),
350 External(#[serde(with = "serde_bytes")] Vec<u8>),
351}
352
353impl<T: NodeType, S> MessageType<T, S> {
354 #[cfg(test)]
355 pub fn into_unchecked(self) -> MessageType<T, Unchecked> {
356 match self {
357 Self::Consensus(c) => MessageType::Consensus(c.into_unchecked()),
358 Self::Block(b) => MessageType::Block(b),
359 Self::ProposalFetch(r) => MessageType::ProposalFetch(r),
360 Self::External(v) => MessageType::External(v),
361 }
362 }
363}
364
365#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
366#[serde(bound(deserialize = "S: Deserialize<'de>"))]
367pub struct Message<T: NodeType, S> {
368 pub sender: T::SignatureKey,
369 pub message_type: MessageType<T, S>,
370}
371
372impl<T: NodeType, S> Message<T, S> {
373 pub fn is_external(&self) -> bool {
374 matches!(self.message_type, MessageType::External(_))
375 }
376
377 #[cfg(test)]
378 pub fn into_unchecked(self) -> Message<T, Unchecked> {
379 Message {
380 sender: self.sender,
381 message_type: self.message_type.into_unchecked(),
382 }
383 }
384}
385
386impl<T: NodeType, S> HasViewNumber for Message<T, S> {
387 fn view_number(&self) -> ViewNumber {
388 match &self.message_type {
389 MessageType::Consensus(consensus_message) => consensus_message.view_number(),
390 MessageType::Block(block_message) => block_message.view_number(),
391 MessageType::ProposalFetch(message) => message.view_number(),
392 MessageType::External(_) => ViewNumber::new(1), }
394 }
395}
396
397pub struct OpaqueMessage<K> {
398 pub sender: K,
399 pub data: Vec<u8>,
400}