hashgraph_like_consensus/
types.rs1use std::time::Duration;
7
8use crate::{
9 error::ConsensusError,
10 protos::consensus::v1::Proposal,
11 utils::{current_timestamp, generate_id, validate_expected_voters_count, validate_timeout},
12};
13
14#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum ConsensusEvent {
17 ConsensusReached {
19 proposal_id: u32,
20 result: bool,
21 timestamp: u64,
22 },
23 ConsensusFailed { proposal_id: u32, timestamp: u64 },
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum SessionTransition {
30 StillActive,
32 ConsensusReached(bool),
34}
35
36#[derive(Debug, Clone)]
42pub struct CreateProposalRequest {
43 pub name: String,
45 pub payload: Vec<u8>,
47 pub proposal_owner: Vec<u8>,
49 pub expected_voters_count: u32,
51 pub expiration_timestamp: u64,
53 pub liveness_criteria_yes: bool,
55}
56
57impl CreateProposalRequest {
58 pub fn new(
60 name: String,
61 payload: Vec<u8>,
62 proposal_owner: Vec<u8>,
63 expected_voters_count: u32,
64 expiration_timestamp: u64,
65 liveness_criteria_yes: bool,
66 ) -> Result<Self, ConsensusError> {
67 validate_expected_voters_count(expected_voters_count)?;
68 validate_timeout(Duration::from_secs(expiration_timestamp))?;
69 let request = Self {
70 name,
71 payload,
72 proposal_owner,
73 expected_voters_count,
74 expiration_timestamp,
75 liveness_criteria_yes,
76 };
77 Ok(request)
78 }
79
80 pub fn into_proposal(self) -> Result<Proposal, ConsensusError> {
85 let proposal_id = generate_id();
86 let now = current_timestamp()?;
87
88 Ok(Proposal {
89 name: self.name,
90 payload: self.payload,
91 proposal_id,
92 proposal_owner: self.proposal_owner,
93 votes: vec![],
94 expected_voters_count: self.expected_voters_count,
95 round: 1,
96 timestamp: now,
97 expiration_timestamp: now.saturating_add(self.expiration_timestamp),
98 liveness_criteria_yes: self.liveness_criteria_yes,
99 })
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::CreateProposalRequest;
106
107 #[test]
108 fn into_proposal_should_not_overflow_expiration_timestamp() {
109 let request = CreateProposalRequest::new(
110 "overflow-check".to_string(),
111 vec![],
112 vec![1u8; 20],
113 1,
114 u64::MAX,
115 true,
116 )
117 .expect("request should be valid");
118
119 let proposal = request
122 .into_proposal()
123 .expect("proposal creation should handle large expiration safely");
124
125 assert!(
126 proposal.expiration_timestamp >= proposal.timestamp,
127 "expiration must not overflow below creation timestamp"
128 );
129 }
130}