use std::time::Duration;
use crate::{
error::ConsensusError,
protos::consensus::v1::Proposal,
utils::{current_timestamp, generate_id, validate_expected_voters_count, validate_timeout},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConsensusEvent {
ConsensusReached {
proposal_id: u32,
result: bool,
timestamp: u64,
},
ConsensusFailed { proposal_id: u32, timestamp: u64 },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SessionTransition {
StillActive,
ConsensusReached(bool),
}
#[derive(Debug, Clone)]
pub struct CreateProposalRequest {
pub name: String,
pub payload: Vec<u8>,
pub proposal_owner: Vec<u8>,
pub expected_voters_count: u32,
pub expiration_timestamp: u64,
pub liveness_criteria_yes: bool,
}
impl CreateProposalRequest {
pub fn new(
name: String,
payload: Vec<u8>,
proposal_owner: Vec<u8>,
expected_voters_count: u32,
expiration_timestamp: u64,
liveness_criteria_yes: bool,
) -> Result<Self, ConsensusError> {
validate_expected_voters_count(expected_voters_count)?;
validate_timeout(Duration::from_secs(expiration_timestamp))?;
let request = Self {
name,
payload,
proposal_owner,
expected_voters_count,
expiration_timestamp,
liveness_criteria_yes,
};
Ok(request)
}
pub fn into_proposal(self) -> Result<Proposal, ConsensusError> {
let proposal_id = generate_id();
let now = current_timestamp()?;
Ok(Proposal {
name: self.name,
payload: self.payload,
proposal_id,
proposal_owner: self.proposal_owner,
votes: vec![],
expected_voters_count: self.expected_voters_count,
round: 1,
timestamp: now,
expiration_timestamp: now.saturating_add(self.expiration_timestamp),
liveness_criteria_yes: self.liveness_criteria_yes,
})
}
}
#[cfg(test)]
mod tests {
use super::CreateProposalRequest;
#[test]
fn into_proposal_should_not_overflow_expiration_timestamp() {
let request = CreateProposalRequest::new(
"overflow-check".to_string(),
vec![],
vec![1u8; 20],
1,
u64::MAX,
true,
)
.expect("request should be valid");
let proposal = request
.into_proposal()
.expect("proposal creation should handle large expiration safely");
assert!(
proposal.expiration_timestamp >= proposal.timestamp,
"expiration must not overflow below creation timestamp"
);
}
}