use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::fmt;
use uuid::Uuid;
pub mod bft;
pub mod quorum;
pub mod reputation;
pub mod voting;
pub use bft::{BftConfig, BftConsensus};
pub use quorum::{Quorum, QuorumConfig};
pub use reputation::{ReputationConfig, ReputationEntry, ReputationSystem};
pub use voting::{VoteCollector, VotingConfig};
use crate::agents::VerificationAgent;
use crate::crypto::Signature;
use ed25519_dalek::VerifyingKey;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct RoundId(pub u64);
impl fmt::Display for RoundId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Round({})", self.0)
}
}
impl RoundId {
pub fn next(self) -> Self {
RoundId(self.0 + 1)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct AuthorityId(pub String);
impl fmt::Display for AuthorityId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<String> for AuthorityId {
fn from(s: String) -> Self {
AuthorityId(s)
}
}
impl From<&str> for AuthorityId {
fn from(s: &str) -> Self {
AuthorityId(s.to_string())
}
}
impl From<Uuid> for AuthorityId {
fn from(uuid: Uuid) -> Self {
AuthorityId(uuid.to_string())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct VoteValue(pub Vec<u8>);
impl VoteValue {
pub fn new(data: Vec<u8>) -> Self {
VoteValue(data)
}
pub fn from_string(s: &str) -> Self {
VoteValue(s.as_bytes().to_vec())
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Vote {
pub round_id: RoundId,
pub authority: AuthorityId,
pub value: VoteValue,
pub weight: u64,
pub timestamp: u64,
pub signature: Vec<u8>,
}
impl Vote {
pub fn new(round_id: RoundId, authority: AuthorityId, value: VoteValue, weight: u64) -> Self {
Vote {
round_id,
authority,
value,
weight,
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
signature: Vec::new(),
}
}
pub fn with_signature(mut self, signature: Vec<u8>) -> Self {
self.signature = signature;
self
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Authority {
pub id: AuthorityId,
pub weight: u64,
pub reputation: f64,
pub is_byzantine: bool,
}
impl Authority {
pub fn new(id: AuthorityId, weight: u64) -> Self {
Authority {
id,
weight,
reputation: 1.0,
is_byzantine: false,
}
}
pub fn with_reputation(mut self, reputation: f64) -> Self {
self.reputation = reputation;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ConsensusPhase {
Idle,
PrePrepare,
Prepare,
Commit,
Decided,
ViewChange,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BftConsensusResult {
pub round_id: RoundId,
pub value: VoteValue,
pub total_weight: u64,
pub participating_authorities: HashSet<AuthorityId>,
pub phase: ConsensusPhase,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConsensusResult {
pub reached: bool,
pub votes_for: usize,
pub total_votes: usize,
pub threshold: f64,
pub agents: Vec<Uuid>,
}
impl ConsensusResult {
pub fn is_valid(&self) -> bool {
self.reached
}
pub fn percentage(&self) -> f64 {
if self.total_votes == 0 {
0.0
} else {
self.votes_for as f64 / self.total_votes as f64
}
}
}
pub trait Consensus: Send + Sync {
fn submit_vote(&mut self, vote: Vote) -> Result<()>;
fn has_consensus(&self) -> bool;
fn get_result(&self) -> Option<BftConsensusResult>;
fn get_phase(&self) -> ConsensusPhase;
fn start_round(&mut self, round_id: RoundId, value: VoteValue) -> Result<()>;
fn current_round(&self) -> RoundId;
fn authorities(&self) -> Vec<Authority>;
fn handle_timeout(&mut self) -> Result<()>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentVote {
pub agent_id: Uuid,
pub vote: bool,
pub message: Option<String>,
}
impl AgentVote {
pub fn approve(agent_id: Uuid) -> Self {
Self {
agent_id,
vote: true,
message: None,
}
}
pub fn reject(agent_id: Uuid, message: String) -> Self {
Self {
agent_id,
vote: false,
message: Some(message),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_consensus_result() {
let result = ConsensusResult {
reached: true,
votes_for: 7,
total_votes: 10,
threshold: 0.67,
agents: vec![Uuid::new_v4(); 10],
};
assert!(result.is_valid());
assert_eq!(result.percentage(), 0.7);
}
#[test]
fn test_agent_vote() {
let approve = AgentVote::approve(Uuid::new_v4());
assert!(approve.vote);
assert!(approve.message.is_none());
let reject = AgentVote::reject(Uuid::new_v4(), "Invalid signature".to_string());
assert!(!reject.vote);
assert!(reject.message.is_some());
}
}
#[derive(Debug, Clone)]
pub struct ConsensusConfig {
pub threshold: f64,
pub timeout_ms: u64,
}
impl ConsensusConfig {
pub fn new(threshold: f64, timeout_ms: u64) -> Result<Self> {
if !(0.0..=1.0).contains(&threshold) {
return Err(Error::config("Consensus threshold must be between 0.0 and 1.0"));
}
Ok(Self {
threshold,
timeout_ms,
})
}
}
pub struct ConsensusEngine {
config: ConsensusConfig,
}
impl ConsensusEngine {
pub fn new(config: ConsensusConfig) -> Self {
Self { config }
}
pub async fn verify_with_consensus(
&self,
agents: Vec<Arc<dyn VerificationAgent>>,
signature: Signature,
message: &[u8],
public_key: &VerifyingKey,
) -> Result<ConsensusResult> {
if agents.is_empty() {
return Err(Error::agent_pool("No agents available for consensus"));
}
let total_votes = agents.len();
let mut votes_for = 0;
let mut agent_ids = Vec::with_capacity(total_votes);
let sig_bytes = signature.to_bytes();
for agent in &agents {
agent_ids.push(agent.id());
match agent.verify(message, &sig_bytes, public_key).await {
Ok(true) => votes_for += 1,
Ok(false) => {
tracing::debug!("Agent {} voted invalid", agent.id());
}
Err(e) => {
tracing::warn!("Agent {} verification error: {}", agent.id(), e);
}
}
}
let reached = (votes_for as f64 / total_votes as f64) >= self.config.threshold;
Ok(ConsensusResult {
reached,
votes_for,
total_votes,
threshold: self.config.threshold,
agents: agent_ids,
})
}
}