use crate::types::{Outcome, Time};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ReplicaId(pub String);
impl fmt::Display for ReplicaId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "replica:{}", self.0)
}
}
impl ReplicaId {
pub fn new(id: String) -> Self {
Self(id)
}
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct ViewNumber(pub u64);
impl ViewNumber {
pub fn new(v: u64) -> Self {
Self(v)
}
pub fn primary(&self, replica_count: usize) -> usize {
(self.0 as usize) % replica_count
}
pub fn next(self) -> Self {
Self(self.0 + 1)
}
}
impl fmt::Display for ViewNumber {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "view:{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct SequenceNumber(pub u64);
impl SequenceNumber {
pub fn new(n: u64) -> Self {
Self(n)
}
pub fn next(self) -> Self {
Self(self.0 + 1)
}
}
impl fmt::Display for SequenceNumber {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "seq:{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct MessageDigest(pub [u8; 32]);
impl MessageDigest {
pub fn of<T: Serialize>(data: &T) -> crate::error::Result<Self> {
use sha2::{Digest, Sha256};
let serialized = serde_json::to_vec(data)
.map_err(|_| crate::error::Error::new(crate::error::ErrorKind::InvalidInput))?;
let mut hasher = Sha256::new();
hasher.update(&serialized);
let result = hasher.finalize();
let mut digest = [0u8; 32];
digest.copy_from_slice(&result[..]);
Ok(Self(digest))
}
pub fn from_bytes(bytes: [u8; 32]) -> Self {
Self(bytes)
}
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
}
impl fmt::Display for MessageDigest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "digest:{}", hex::encode(&self.0[..8]))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PhaseKind {
PrePrepare,
Prepare,
Commit,
ViewChange,
NewView,
}
impl fmt::Display for PhaseKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PhaseKind::PrePrepare => write!(f, "pre-prepare"),
PhaseKind::Prepare => write!(f, "prepare"),
PhaseKind::Commit => write!(f, "commit"),
PhaseKind::ViewChange => write!(f, "view-change"),
PhaseKind::NewView => write!(f, "new-view"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConsensusRequest {
pub client_id: String,
pub timestamp: Time,
pub operation: Vec<u8>,
}
impl ConsensusRequest {
pub fn new(client_id: String, timestamp: Time, operation: Vec<u8>) -> Self {
Self {
client_id,
timestamp,
operation,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConsensusResponse {
pub view: ViewNumber,
pub sequence: SequenceNumber,
pub result: Outcome<Vec<u8>, String>,
pub replica_id: ReplicaId,
pub timestamp: Time,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConsensusBatch {
pub requests: Vec<ConsensusRequest>,
pub timestamp: Time,
}
impl ConsensusBatch {
pub fn new(requests: Vec<ConsensusRequest>) -> Self {
Self {
requests,
timestamp: Time::from_millis(0),
}
}
pub fn is_empty(&self) -> bool {
self.requests.is_empty()
}
pub fn len(&self) -> usize {
self.requests.len()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ConsensusError {
InsufficientReplicas { required: usize, available: usize },
InvalidView {
expected: ViewNumber,
received: ViewNumber,
},
InvalidSequence {
expected: SequenceNumber,
received: SequenceNumber,
},
AuthenticationFailure {
replica_id: ReplicaId,
reason: String,
},
Timeout { phase: PhaseKind, duration_ms: u64 },
ByzantineDetected {
replica_id: ReplicaId,
evidence: String,
},
ViewChangeInProgress {
current_view: ViewNumber,
target_view: ViewNumber,
},
NotPrimary {
replica_id: ReplicaId,
view: ViewNumber,
primary: usize,
},
ConsensusFailed { reason: String },
}
impl fmt::Display for ConsensusError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConsensusError::InsufficientReplicas {
required,
available,
} => {
write!(
f,
"insufficient replicas: need {}, have {}",
required, available
)
}
ConsensusError::InvalidView { expected, received } => {
write!(f, "invalid view: expected {}, got {}", expected, received)
}
ConsensusError::InvalidSequence { expected, received } => {
write!(
f,
"invalid sequence: expected {}, got {}",
expected, received
)
}
ConsensusError::AuthenticationFailure { replica_id, reason } => {
write!(f, "authentication failed for {}: {}", replica_id, reason)
}
ConsensusError::Timeout { phase, duration_ms } => {
write!(f, "{} phase timeout after {}ms", phase, duration_ms)
}
ConsensusError::ByzantineDetected {
replica_id,
evidence,
} => {
write!(f, "Byzantine behavior from {}: {}", replica_id, evidence)
}
ConsensusError::ViewChangeInProgress {
current_view,
target_view,
} => {
write!(
f,
"view change in progress: {} -> {}",
current_view, target_view
)
}
ConsensusError::NotPrimary {
replica_id,
view,
primary,
} => {
write!(
f,
"{} not primary for {}, primary is replica {}",
replica_id, view, primary
)
}
ConsensusError::ConsensusFailed { reason } => {
write!(f, "consensus failed: {}", reason)
}
}
}
}
impl std::error::Error for ConsensusError {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageCertificate {
pub digest: MessageDigest,
pub signatures: HashMap<ReplicaId, Vec<u8>>,
pub view: ViewNumber,
}
impl MessageCertificate {
pub fn new(digest: MessageDigest, view: ViewNumber) -> Self {
Self {
digest,
signatures: HashMap::new(),
view,
}
}
pub fn add_signature(&mut self, replica_id: ReplicaId, signature: Vec<u8>) {
self.signatures.insert(replica_id, signature);
}
pub fn is_valid(&self, f: usize) -> bool {
self.signatures.len() > 2 * f
}
}