#![deny(unsafe_code)]
#![warn(missing_docs)]
pub mod consensus;
pub mod dag;
pub mod edge;
pub mod error;
pub mod graph;
pub mod node;
pub mod tip_selection;
pub mod vertex;
#[cfg(test)]
mod consensus_tests;
#[cfg(test)]
mod invariant_tests;
#[cfg(test)]
mod module_exports_tests;
#[cfg(test)]
mod lib_test_compilation;
pub type Result<T> = std::result::Result<T, error::DagError>;
pub use edge::Edge;
pub use error::DagError;
pub use graph::{Graph, GraphMetrics, StorageConfig};
pub use node::{Node, NodeState, SerializableHash};
pub use consensus::{
Confidence, Consensus, ConsensusError, ConsensusMetrics, ConsensusStatus, QRAvalanche,
QRAvalancheConfig, VotingRecord,
};
pub use dag::{Dag, DagError as DagModuleError, DagMessage};
pub use tip_selection::{
AdvancedTipSelection, ParentSelectionAlgorithm, TipSelection, TipSelectionConfig,
TipSelectionError, VertexWeight,
};
pub use vertex::{Vertex, VertexError, VertexId, VertexOps};
pub type QrDag = DAGConsensus;
use std::collections::HashSet;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct ConsensusConfig {
pub query_sample_size: usize,
pub finality_threshold: f64,
pub finality_timeout: Duration,
pub confirmation_depth: usize,
}
impl Default for ConsensusConfig {
fn default() -> Self {
Self {
query_sample_size: 10,
finality_threshold: 0.8,
finality_timeout: Duration::from_secs(5),
confirmation_depth: 3,
}
}
}
pub struct DAGConsensus {
dag: Dag,
#[allow(dead_code)]
config: ConsensusConfig,
consensus: QRAvalanche,
}
impl Default for DAGConsensus {
fn default() -> Self {
Self::new()
}
}
impl DAGConsensus {
pub fn new() -> Self {
Self::with_config(ConsensusConfig::default())
}
pub fn with_config(config: ConsensusConfig) -> Self {
Self {
dag: Dag::new(100), config,
consensus: QRAvalanche::new(),
}
}
pub fn add_vertex(&mut self, vertex: Vertex) -> Result<()> {
let vertex_id_str = String::from_utf8_lossy(vertex.id.as_bytes()).to_string();
if self.consensus.vertices.contains_key(&vertex.id) {
return Err(DagError::ConsensusError(format!(
"Fork detected: vertex {} already exists",
vertex_id_str
)));
}
if !vertex.parents.is_empty() {
for parent in &vertex.parents {
if !self.consensus.vertices.contains_key(parent) {
return Err(DagError::ConsensusError(format!(
"Invalid vertex: parent {:?} not found",
parent
)));
}
}
}
if vertex.parents.contains(&vertex.id) {
return Err(DagError::ConsensusError(format!(
"Validation error: vertex {} references itself",
vertex_id_str
)));
}
self.consensus
.vertices
.insert(vertex.id.clone(), ConsensusStatus::Final);
self.consensus.tips.insert(vertex.id.clone());
let msg = DagMessage {
id: vertex.id.clone(),
payload: vertex.payload.clone(),
parents: vertex.parents(),
timestamp: vertex.timestamp,
};
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async { self.dag.submit_message(msg).await })
.map_err(|e| match e {
dag::DagError::VertexError(_) => {
DagError::ConsensusError(format!("Invalid vertex: {}", e))
}
dag::DagError::ConflictDetected => {
DagError::ConsensusError("Conflict detected".to_string())
}
_ => DagError::ConsensusError(format!("DAG error: {}", e)),
})?;
Ok(())
}
pub fn get_confidence(&self, vertex_id: &str) -> Option<ConsensusStatus> {
let id = VertexId::from_bytes(vertex_id.as_bytes().to_vec());
self.consensus.vertices.get(&id).cloned()
}
pub fn get_total_order(&self) -> Result<Vec<String>> {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let vertices = self.dag.vertices.read().await;
let mut ordered: Vec<_> = vertices.values().collect();
ordered.sort_by_key(|v| v.timestamp);
Ok(ordered
.iter()
.map(|v| String::from_utf8_lossy(v.id.as_bytes()).to_string())
.collect())
})
}
pub fn get_tips(&self) -> Vec<String> {
self.consensus
.tips
.iter()
.map(|id| String::from_utf8_lossy(id.as_bytes()).to_string())
.collect()
}
pub fn add_message(&mut self, message: Vec<u8>) -> Result<()> {
let vertex_id = VertexId::from_bytes(message.clone());
let vertex = Vertex::new(vertex_id, message, HashSet::new());
self.add_vertex(vertex)
}
pub fn contains_message(&self, message: &[u8]) -> bool {
let vertex_id = VertexId::from_bytes(message.to_vec());
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async { self.dag.vertices.read().await.contains_key(&vertex_id) })
}
pub fn verify_message(&self, _message: &[u8], _public_key: &[u8]) -> bool {
true
}
}