Skip to main content

hanzo_mining/
consensus.rs

1//! Native Lux Consensus Integration for AI-Chain
2//!
3//! This module provides native consensus capabilities when the `consensus` feature
4//! is enabled, allowing hanzo-mining to operate as a sovereign L1 "AI-Chain".
5//!
6//! ## Architecture
7//!
8//! The AI-Chain is a specialized blockchain for AI mining:
9//! - Native ML-DSA quantum-safe signatures
10//! - 500ms block time with 69% BFT quorum
11//! - 2-round finality for fast confirmation
12//! - Cross-chain teleport to EVM L2s (Hanzo, Zoo, Lux C-Chain)
13//!
14//! ## Feature Flags
15//!
16//! - `consensus`: Enable embedded Lux consensus (native L1 mode)
17//! - Default: RPC-based consensus (connects to existing nodes)
18//!
19//! ## Usage
20//!
21//! ```ignore
22//! use hanzo_mining::consensus::{ConsensusEngine, ConsensusConfig};
23//!
24//! // Create AI-Chain consensus engine
25//! let config = ConsensusConfig::ai_chain_mainnet();
26//! let mut engine = ConsensusEngine::new(config)?;
27//! engine.start()?;
28//!
29//! // Submit blocks and votes
30//! let block = engine.propose_block(transactions)?;
31//! engine.broadcast_vote(block.id, VoteType::Preference)?;
32//! ```
33
34use serde::{Deserialize, Serialize};
35use std::sync::Arc;
36use tokio::sync::RwLock;
37
38#[cfg(feature = "consensus")]
39use lux_consensus::{Block as LuxBlock, Chain, Config as LuxConfig, Engine, ID, Vote as LuxVote, VoteType as LuxVoteType};
40
41use crate::ledger::{BlockHeader, MiningTransaction, VoteType, LedgerError};
42
43/// AI-Chain network identifiers
44pub const AI_CHAIN_MAINNET_ID: u64 = 36963;
45pub const AI_CHAIN_TESTNET_ID: u64 = 36964;
46
47/// AI-Chain consensus parameters
48pub const AI_CHAIN_BLOCK_TIME_MS: u64 = 500;
49pub const AI_CHAIN_QUORUM_THRESHOLD: f64 = 0.69;
50pub const AI_CHAIN_FINALITY_ROUNDS: u32 = 2;
51pub const AI_CHAIN_SAMPLE_SIZE: usize = 20;
52
53/// Consensus configuration for AI-Chain
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct ConsensusConfig {
56    /// Network identifier
57    pub network_id: u64,
58    /// Enable quantum-safe cryptography
59    pub quantum_resistant: bool,
60    /// Security level (NIST 2, 3, or 5)
61    pub security_level: u32,
62    /// Sample size for voting (k parameter)
63    pub sample_size: usize,
64    /// Quorum size (alpha parameter)
65    pub quorum_size: usize,
66    /// Block time in milliseconds
67    pub block_time_ms: u64,
68    /// Enable GPU acceleration for signature verification
69    pub gpu_acceleration: bool,
70    /// Bootstrap peers for P2P network
71    pub bootstrap_peers: Vec<String>,
72}
73
74impl ConsensusConfig {
75    /// AI-Chain mainnet configuration
76    pub fn ai_chain_mainnet() -> Self {
77        Self {
78            network_id: AI_CHAIN_MAINNET_ID,
79            quantum_resistant: true,
80            security_level: 3, // NIST Level 3 (ML-DSA-65)
81            sample_size: AI_CHAIN_SAMPLE_SIZE,
82            quorum_size: AI_CHAIN_SAMPLE_SIZE, // 69% of validators
83            block_time_ms: AI_CHAIN_BLOCK_TIME_MS,
84            gpu_acceleration: true,
85            bootstrap_peers: vec![
86                "/dns4/boot1.ai-chain.hanzo.network/tcp/3691".to_string(),
87                "/dns4/boot2.ai-chain.hanzo.network/tcp/3691".to_string(),
88                "/dns4/boot3.ai-chain.hanzo.network/tcp/3691".to_string(),
89            ],
90        }
91    }
92
93    /// AI-Chain testnet configuration
94    pub fn ai_chain_testnet() -> Self {
95        Self {
96            network_id: AI_CHAIN_TESTNET_ID,
97            quantum_resistant: true,
98            security_level: 3,
99            sample_size: AI_CHAIN_SAMPLE_SIZE,
100            quorum_size: AI_CHAIN_SAMPLE_SIZE,
101            block_time_ms: AI_CHAIN_BLOCK_TIME_MS,
102            gpu_acceleration: false,
103            bootstrap_peers: vec![
104                "/dns4/boot1.ai-chain-test.hanzo.network/tcp/3691".to_string(),
105            ],
106        }
107    }
108
109    /// Convert to lux-consensus Config
110    #[cfg(feature = "consensus")]
111    pub fn to_lux_config(&self) -> LuxConfig {
112        LuxConfig {
113            alpha: self.quorum_size,
114            k: self.sample_size,
115            max_outstanding: 10,
116            max_poll_delay: std::time::Duration::from_millis(self.block_time_ms),
117            network_timeout: std::time::Duration::from_secs(5),
118            max_message_size: 2 * 1024 * 1024,
119            security_level: self.security_level,
120            quantum_resistant: self.quantum_resistant,
121            gpu_acceleration: self.gpu_acceleration,
122        }
123    }
124}
125
126/// AI-Chain block
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct AIChainBlock {
129    /// Block identifier (Blake3 hash)
130    pub id: [u8; 32],
131    /// Parent block hash
132    pub parent_id: [u8; 32],
133    /// Block height
134    pub height: u64,
135    /// Block timestamp (Unix ms)
136    pub timestamp: u64,
137    /// Proposer's quantum-safe public key
138    pub proposer: Vec<u8>,
139    /// Mining transactions in this block
140    pub transactions: Vec<MiningTransaction>,
141    /// Merkle root of transactions
142    pub tx_root: [u8; 32],
143    /// Merkle root of state
144    pub state_root: [u8; 32],
145    /// Proposer signature (ML-DSA)
146    pub signature: Vec<u8>,
147}
148
149impl AIChainBlock {
150    /// Create a new block
151    pub fn new(
152        parent_id: [u8; 32],
153        height: u64,
154        proposer: Vec<u8>,
155        transactions: Vec<MiningTransaction>,
156    ) -> Self {
157        let timestamp = std::time::SystemTime::now()
158            .duration_since(std::time::UNIX_EPOCH)
159            .unwrap()
160            .as_millis() as u64;
161
162        // Compute transaction Merkle root
163        let tx_root = compute_tx_root(&transactions);
164
165        // Compute block ID
166        let id = compute_block_id(parent_id, height, timestamp, &tx_root);
167
168        Self {
169            id,
170            parent_id,
171            height,
172            timestamp,
173            proposer,
174            transactions,
175            tx_root,
176            state_root: [0u8; 32], // Computed after state application
177            signature: Vec::new(),
178        }
179    }
180
181    /// Sign the block with ML-DSA
182    pub fn sign(&mut self, signature: Vec<u8>) {
183        self.signature = signature;
184    }
185
186    /// Convert to BlockHeader for ledger
187    pub fn to_header(&self) -> BlockHeader {
188        BlockHeader {
189            height: self.height,
190            parent_hash: self.parent_id,
191            hash: self.id,
192            timestamp: self.timestamp,
193            proposer: self.proposer.clone(),
194            tx_root: self.tx_root,
195            state_root: self.state_root,
196            tx_count: self.transactions.len() as u32,
197        }
198    }
199
200    /// Convert to lux-consensus Block
201    #[cfg(feature = "consensus")]
202    pub fn to_lux_block(&self) -> LuxBlock {
203        let payload = serde_json::to_vec(&self.transactions).unwrap_or_default();
204        LuxBlock::new(
205            ID::from(self.id),
206            ID::from(self.parent_id),
207            self.height,
208            payload,
209        )
210    }
211}
212
213/// AI-Chain vote
214#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct AIChainVote {
216    /// Block being voted on
217    pub block_id: [u8; 32],
218    /// Vote type
219    pub vote_type: VoteType,
220    /// Voter's quantum-safe public key
221    pub voter: Vec<u8>,
222    /// Vote signature (ML-DSA)
223    pub signature: Vec<u8>,
224}
225
226impl AIChainVote {
227    /// Create a new vote
228    pub fn new(block_id: [u8; 32], vote_type: VoteType, voter: Vec<u8>) -> Self {
229        Self {
230            block_id,
231            vote_type,
232            voter,
233            signature: Vec::new(),
234        }
235    }
236
237    /// Sign the vote with ML-DSA
238    pub fn sign(&mut self, signature: Vec<u8>) {
239        self.signature = signature;
240    }
241
242    /// Convert to lux-consensus Vote
243    #[cfg(feature = "consensus")]
244    pub fn to_lux_vote(&self) -> LuxVote {
245        let lux_vote_type = match self.vote_type {
246            VoteType::Preference => LuxVoteType::Preference,
247            VoteType::Commit => LuxVoteType::Commit,
248            VoteType::Cancel => LuxVoteType::Cancel,
249        };
250
251        LuxVote::new(
252            ID::from(self.block_id),
253            lux_vote_type,
254            ID::new(self.voter.clone()),
255        )
256        .with_signature(self.signature.clone())
257    }
258}
259
260/// Consensus engine mode
261#[derive(Debug, Clone, PartialEq)]
262pub enum ConsensusMode {
263    /// Embedded consensus (native L1)
264    #[cfg(feature = "consensus")]
265    Embedded,
266    /// RPC-based consensus (client mode)
267    Rpc { endpoint: String },
268}
269
270/// Native consensus engine for AI-Chain
271pub struct ConsensusEngine {
272    /// Configuration
273    config: ConsensusConfig,
274    /// Operating mode
275    mode: ConsensusMode,
276    /// Embedded consensus chain (when feature enabled)
277    #[cfg(feature = "consensus")]
278    chain: Option<Chain>,
279    /// Current block height
280    height: Arc<RwLock<u64>>,
281    /// Is engine running
282    running: Arc<RwLock<bool>>,
283    /// Pending blocks awaiting finality
284    pending_blocks: Arc<RwLock<Vec<AIChainBlock>>>,
285    /// Accepted blocks
286    accepted_blocks: Arc<RwLock<Vec<[u8; 32]>>>,
287}
288
289impl ConsensusEngine {
290    /// Create new consensus engine
291    pub fn new(config: ConsensusConfig) -> Result<Self, ConsensusError> {
292        #[cfg(feature = "consensus")]
293        {
294            let lux_config = config.to_lux_config();
295            let chain = Chain::new(lux_config);
296
297            Ok(Self {
298                config,
299                mode: ConsensusMode::Embedded,
300                chain: Some(chain),
301                height: Arc::new(RwLock::new(0)),
302                running: Arc::new(RwLock::new(false)),
303                pending_blocks: Arc::new(RwLock::new(Vec::new())),
304                accepted_blocks: Arc::new(RwLock::new(Vec::new())),
305            })
306        }
307
308        #[cfg(not(feature = "consensus"))]
309        {
310            Ok(Self {
311                config,
312                mode: ConsensusMode::Rpc {
313                    endpoint: "https://consensus.hanzo.network".to_string(),
314                },
315                height: Arc::new(RwLock::new(0)),
316                running: Arc::new(RwLock::new(false)),
317                pending_blocks: Arc::new(RwLock::new(Vec::new())),
318                accepted_blocks: Arc::new(RwLock::new(Vec::new())),
319            })
320        }
321    }
322
323    /// Create engine with specific RPC endpoint
324    pub fn with_rpc(config: ConsensusConfig, endpoint: String) -> Self {
325        Self {
326            config,
327            mode: ConsensusMode::Rpc { endpoint },
328            #[cfg(feature = "consensus")]
329            chain: None,
330            height: Arc::new(RwLock::new(0)),
331            running: Arc::new(RwLock::new(false)),
332            pending_blocks: Arc::new(RwLock::new(Vec::new())),
333            accepted_blocks: Arc::new(RwLock::new(Vec::new())),
334        }
335    }
336
337    /// Start the consensus engine
338    pub async fn start(&mut self) -> Result<(), ConsensusError> {
339        #[cfg(feature = "consensus")]
340        if let Some(ref mut chain) = self.chain {
341            chain.start().map_err(|e| ConsensusError::EngineError(e.to_string()))?;
342        }
343
344        *self.running.write().await = true;
345        Ok(())
346    }
347
348    /// Stop the consensus engine
349    pub async fn stop(&mut self) -> Result<(), ConsensusError> {
350        #[cfg(feature = "consensus")]
351        if let Some(ref mut chain) = self.chain {
352            chain.stop().map_err(|e| ConsensusError::EngineError(e.to_string()))?;
353        }
354
355        *self.running.write().await = false;
356        Ok(())
357    }
358
359    /// Submit a block for consensus
360    pub async fn submit_block(&mut self, block: AIChainBlock) -> Result<(), ConsensusError> {
361        if !*self.running.read().await {
362            return Err(ConsensusError::NotRunning);
363        }
364
365        #[cfg(feature = "consensus")]
366        if let Some(ref mut chain) = self.chain {
367            let lux_block = block.to_lux_block();
368            chain.add(lux_block).map_err(|e| ConsensusError::EngineError(e.to_string()))?;
369        }
370
371        // Track pending block
372        self.pending_blocks.write().await.push(block);
373
374        Ok(())
375    }
376
377    /// Record a vote
378    pub async fn record_vote(&mut self, vote: AIChainVote) -> Result<bool, ConsensusError> {
379        if !*self.running.read().await {
380            return Err(ConsensusError::NotRunning);
381        }
382
383        #[cfg(feature = "consensus")]
384        if let Some(ref mut chain) = self.chain {
385            let lux_vote = vote.to_lux_vote();
386            chain.record_vote(lux_vote).map_err(|e| ConsensusError::EngineError(e.to_string()))?;
387
388            // Check if block is now accepted
389            let id = ID::from(vote.block_id);
390            if chain.is_accepted(&id) {
391                self.accepted_blocks.write().await.push(vote.block_id);
392                return Ok(true);
393            }
394        }
395
396        Ok(false)
397    }
398
399    /// Check if a block is accepted (finalized)
400    pub async fn is_accepted(&self, block_id: &[u8; 32]) -> bool {
401        #[cfg(feature = "consensus")]
402        if let Some(ref chain) = self.chain {
403            let id = ID::from(*block_id);
404            return chain.is_accepted(&id);
405        }
406
407        self.accepted_blocks.read().await.contains(block_id)
408    }
409
410    /// Get current block height
411    pub async fn get_height(&self) -> u64 {
412        *self.height.read().await
413    }
414
415    /// Get consensus mode
416    pub fn mode(&self) -> &ConsensusMode {
417        &self.mode
418    }
419
420    /// Get configuration
421    pub fn config(&self) -> &ConsensusConfig {
422        &self.config
423    }
424
425    /// Check if quantum-safe mode is enabled
426    pub fn is_quantum_safe(&self) -> bool {
427        self.config.quantum_resistant
428    }
429}
430
431/// Consensus errors
432#[derive(Debug, thiserror::Error)]
433pub enum ConsensusError {
434    #[error("Consensus engine not running")]
435    NotRunning,
436    #[error("Block not found: {0}")]
437    BlockNotFound(String),
438    #[error("Invalid block: {0}")]
439    InvalidBlock(String),
440    #[error("Invalid vote: {0}")]
441    InvalidVote(String),
442    #[error("Engine error: {0}")]
443    EngineError(String),
444    #[error("Signature verification failed")]
445    SignatureError,
446    #[error("Network error: {0}")]
447    NetworkError(String),
448}
449
450impl From<ConsensusError> for LedgerError {
451    fn from(e: ConsensusError) -> Self {
452        LedgerError::TransactionFailed(e.to_string())
453    }
454}
455
456// ============= Helper Functions =============
457
458/// Compute transaction Merkle root
459fn compute_tx_root(transactions: &[MiningTransaction]) -> [u8; 32] {
460    if transactions.is_empty() {
461        return [0u8; 32];
462    }
463
464    let tx_bytes: Vec<u8> = transactions
465        .iter()
466        .flat_map(|tx| serde_json::to_vec(tx).unwrap_or_default())
467        .collect();
468
469    *blake3::hash(&tx_bytes).as_bytes()
470}
471
472/// Compute block ID from block data
473fn compute_block_id(
474    parent_id: [u8; 32],
475    height: u64,
476    timestamp: u64,
477    tx_root: &[u8; 32],
478) -> [u8; 32] {
479    let mut data = Vec::new();
480    data.extend_from_slice(&parent_id);
481    data.extend_from_slice(&height.to_le_bytes());
482    data.extend_from_slice(&timestamp.to_le_bytes());
483    data.extend_from_slice(tx_root);
484    *blake3::hash(&data).as_bytes()
485}
486
487#[cfg(test)]
488mod tests {
489    use super::*;
490
491    #[test]
492    fn test_config_mainnet() {
493        let config = ConsensusConfig::ai_chain_mainnet();
494        assert_eq!(config.network_id, AI_CHAIN_MAINNET_ID);
495        assert!(config.quantum_resistant);
496        assert_eq!(config.security_level, 3);
497        assert_eq!(config.sample_size, 20);
498        assert!(!config.bootstrap_peers.is_empty());
499    }
500
501    #[test]
502    fn test_config_testnet() {
503        let config = ConsensusConfig::ai_chain_testnet();
504        assert_eq!(config.network_id, AI_CHAIN_TESTNET_ID);
505        assert!(config.quantum_resistant);
506    }
507
508    #[test]
509    fn test_block_creation() {
510        let parent = [0u8; 32];
511        let proposer = vec![1u8; 32];
512        let block = AIChainBlock::new(parent, 1, proposer.clone(), vec![]);
513
514        assert_eq!(block.height, 1);
515        assert_eq!(block.parent_id, parent);
516        assert_eq!(block.proposer, proposer);
517        assert!(block.timestamp > 0);
518    }
519
520    #[test]
521    fn test_vote_creation() {
522        let block_id = [1u8; 32];
523        let voter = vec![2u8; 32];
524        let vote = AIChainVote::new(block_id, VoteType::Preference, voter.clone());
525
526        assert_eq!(vote.block_id, block_id);
527        assert_eq!(vote.vote_type, VoteType::Preference);
528        assert_eq!(vote.voter, voter);
529    }
530
531    #[test]
532    fn test_compute_tx_root() {
533        let empty_root = compute_tx_root(&[]);
534        assert_eq!(empty_root, [0u8; 32]);
535    }
536
537    #[test]
538    fn test_compute_block_id() {
539        let parent = [0u8; 32];
540        let tx_root = [1u8; 32];
541        let id = compute_block_id(parent, 1, 1000, &tx_root);
542
543        // Same inputs should produce same ID
544        let id2 = compute_block_id(parent, 1, 1000, &tx_root);
545        assert_eq!(id, id2);
546
547        // Different inputs should produce different ID
548        let id3 = compute_block_id(parent, 2, 1000, &tx_root);
549        assert_ne!(id, id3);
550    }
551
552    #[tokio::test]
553    async fn test_engine_creation() {
554        let config = ConsensusConfig::ai_chain_testnet();
555        let engine = ConsensusEngine::new(config).unwrap();
556
557        assert!(engine.is_quantum_safe());
558        assert_eq!(engine.get_height().await, 0);
559    }
560
561    #[tokio::test]
562    async fn test_engine_start_stop() {
563        let config = ConsensusConfig::ai_chain_testnet();
564        let mut engine = ConsensusEngine::new(config).unwrap();
565
566        assert!(engine.start().await.is_ok());
567        assert!(engine.stop().await.is_ok());
568    }
569}