1#![deny(unsafe_code)]
2#![warn(missing_docs)]
3
4pub mod consensus;
46pub mod dag;
48pub mod edge;
50pub mod error;
52pub mod graph;
54pub mod node;
56pub mod tip_selection;
61pub mod vertex;
63
64#[cfg(test)]
65mod consensus_tests;
66
67#[cfg(test)]
68mod invariant_tests;
69
70#[cfg(test)]
71mod module_exports_tests;
72
73#[cfg(test)]
74mod lib_test_compilation;
75
76pub type Result<T> = std::result::Result<T, error::DagError>;
78pub use edge::Edge;
79pub use error::DagError;
80pub use graph::{Graph, GraphMetrics, StorageConfig};
81pub use node::{Node, NodeState, SerializableHash};
82
83pub use consensus::{
84 Confidence, Consensus, ConsensusError, ConsensusMetrics, ConsensusStatus, QRAvalanche,
85 QRAvalancheConfig, VotingRecord,
86};
87pub use dag::{Dag, DagError as DagModuleError, DagMessage};
88pub use tip_selection::{
93 AdvancedTipSelection, ParentSelectionAlgorithm, TipSelection, TipSelectionConfig,
94 TipSelectionError, VertexWeight,
95};
96pub use vertex::{Vertex, VertexError, VertexId, VertexOps};
97
98pub type QrDag = DAGConsensus;
100
101use std::collections::HashSet;
104use std::time::Duration;
105
106#[derive(Debug, Clone)]
108pub struct ConsensusConfig {
109 pub query_sample_size: usize,
111 pub finality_threshold: f64,
113 pub finality_timeout: Duration,
115 pub confirmation_depth: usize,
117}
118
119impl Default for ConsensusConfig {
120 fn default() -> Self {
121 Self {
122 query_sample_size: 10,
123 finality_threshold: 0.8,
124 finality_timeout: Duration::from_secs(5),
125 confirmation_depth: 3,
126 }
127 }
128}
129
130pub struct DAGConsensus {
132 dag: Dag,
133 #[allow(dead_code)]
134 config: ConsensusConfig,
135 consensus: QRAvalanche,
136}
137
138impl Default for DAGConsensus {
139 fn default() -> Self {
140 Self::new()
141 }
142}
143
144impl DAGConsensus {
145 pub fn new() -> Self {
147 Self::with_config(ConsensusConfig::default())
148 }
149
150 pub fn with_config(config: ConsensusConfig) -> Self {
152 Self {
153 dag: Dag::new(100), config,
155 consensus: QRAvalanche::new(),
156 }
157 }
158
159 pub fn add_vertex(&mut self, vertex: Vertex) -> Result<()> {
161 let vertex_id_str = String::from_utf8_lossy(vertex.id.as_bytes()).to_string();
163 if self.consensus.vertices.contains_key(&vertex.id) {
164 return Err(DagError::ConsensusError(format!(
165 "Fork detected: vertex {} already exists",
166 vertex_id_str
167 )));
168 }
169
170 if !vertex.parents.is_empty() {
172 for parent in &vertex.parents {
173 if !self.consensus.vertices.contains_key(parent) {
174 return Err(DagError::ConsensusError(format!(
175 "Invalid vertex: parent {:?} not found",
176 parent
177 )));
178 }
179 }
180 }
181
182 if vertex.parents.contains(&vertex.id) {
184 return Err(DagError::ConsensusError(format!(
185 "Validation error: vertex {} references itself",
186 vertex_id_str
187 )));
188 }
189
190 self.consensus
192 .vertices
193 .insert(vertex.id.clone(), ConsensusStatus::Final);
194 self.consensus.tips.insert(vertex.id.clone());
195
196 let msg = DagMessage {
198 id: vertex.id.clone(),
199 payload: vertex.payload.clone(),
200 parents: vertex.parents(),
201 timestamp: vertex.timestamp,
202 };
203
204 let rt = tokio::runtime::Runtime::new().unwrap();
207 rt.block_on(async { self.dag.submit_message(msg).await })
208 .map_err(|e| match e {
209 dag::DagError::VertexError(_) => {
210 DagError::ConsensusError(format!("Invalid vertex: {}", e))
211 }
212 dag::DagError::ConflictDetected => {
213 DagError::ConsensusError("Conflict detected".to_string())
214 }
215 _ => DagError::ConsensusError(format!("DAG error: {}", e)),
216 })?;
217
218 Ok(())
219 }
220
221 pub fn get_confidence(&self, vertex_id: &str) -> Option<ConsensusStatus> {
223 let id = VertexId::from_bytes(vertex_id.as_bytes().to_vec());
224 self.consensus.vertices.get(&id).cloned()
225 }
226
227 pub fn get_total_order(&self) -> Result<Vec<String>> {
229 let rt = tokio::runtime::Runtime::new().unwrap();
231 rt.block_on(async {
232 let vertices = self.dag.vertices.read().await;
233 let mut ordered: Vec<_> = vertices.values().collect();
234 ordered.sort_by_key(|v| v.timestamp);
235 Ok(ordered
236 .iter()
237 .map(|v| String::from_utf8_lossy(v.id.as_bytes()).to_string())
238 .collect())
239 })
240 }
241
242 pub fn get_tips(&self) -> Vec<String> {
244 self.consensus
245 .tips
246 .iter()
247 .map(|id| String::from_utf8_lossy(id.as_bytes()).to_string())
248 .collect()
249 }
250
251 pub fn add_message(&mut self, message: Vec<u8>) -> Result<()> {
253 let vertex_id = VertexId::from_bytes(message.clone());
254 let vertex = Vertex::new(vertex_id, message, HashSet::new());
255 self.add_vertex(vertex)
256 }
257
258 pub fn contains_message(&self, message: &[u8]) -> bool {
260 let vertex_id = VertexId::from_bytes(message.to_vec());
261 let rt = tokio::runtime::Runtime::new().unwrap();
262 rt.block_on(async { self.dag.vertices.read().await.contains_key(&vertex_id) })
263 }
264
265 pub fn verify_message(&self, _message: &[u8], _public_key: &[u8]) -> bool {
267 true
269 }
270}