Skip to main content

hanzo_mining/
bridge.rs

1//! Mining Bridge - Connects ML-DSA Wallet, Lux Consensus Ledger, and Teleport Protocol
2//!
3//! This module integrates the quantum-safe mining wallet with the global ledger
4//! and enables native teleportation of mining rewards to supported EVM chains:
5//! - Lux C-Chain (96369)
6//! - Zoo EVM (200200)
7//! - Hanzo EVM (36963)
8
9use crate::evm::{TeleportDestination, TeleportStatus, TeleportTransfer, ChainConfig, EvmClient};
10use crate::ledger::{MiningLedger, LedgerError};
11use crate::wallet::{MiningWallet, WalletError, SecurityLevel};
12use crate::{MiningRewardType, PerformanceStats};
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15use std::sync::Arc;
16use thiserror::Error;
17use tokio::sync::RwLock;
18
19/// Bridge errors
20#[derive(Debug, Error)]
21pub enum BridgeError {
22    #[error("Wallet error: {0}")]
23    Wallet(#[from] WalletError),
24
25    #[error("Ledger error: {0}")]
26    Ledger(#[from] LedgerError),
27
28    #[error("Insufficient balance: have {have}, need {need}")]
29    InsufficientBalance { have: u128, need: u128 },
30
31    #[error("Teleport failed: {0}")]
32    TeleportFailed(String),
33
34    #[error("Invalid destination chain: {0}")]
35    InvalidDestination(u64),
36
37    #[error("No wallet loaded")]
38    NoWallet,
39
40    #[error("Not connected to ledger")]
41    NotConnected,
42
43    #[error("Network error: {0}")]
44    Network(String),
45}
46
47/// Mining account state
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct MiningAccount {
50    /// Quantum-safe wallet address
51    pub address: String,
52    /// Security level used
53    pub security_level: SecurityLevel,
54    /// Total rewards earned (in wei)
55    pub total_earned: u128,
56    /// Pending rewards to claim
57    pub pending_rewards: u128,
58    /// Rewards already claimed
59    pub claimed_rewards: u128,
60    /// Rewards teleported to EVM by chain
61    pub teleported_rewards: HashMap<TeleportDestination, u128>,
62    /// Active teleport transfers
63    pub active_transfers: Vec<String>,
64    /// Registration timestamp
65    pub registered_at: u64,
66}
67
68impl MiningAccount {
69    pub fn new(address: String, security_level: SecurityLevel) -> Self {
70        Self {
71            address,
72            security_level,
73            total_earned: 0,
74            pending_rewards: 0,
75            claimed_rewards: 0,
76            teleported_rewards: HashMap::new(),
77            active_transfers: Vec::new(),
78            registered_at: current_timestamp(),
79        }
80    }
81
82    /// Available balance (claimed but not teleported)
83    pub fn available_balance(&self) -> u128 {
84        self.claimed_rewards.saturating_sub(
85            self.teleported_rewards.values().sum::<u128>()
86        )
87    }
88}
89
90/// Mining Bridge - connects wallet, ledger, and teleport
91pub struct MiningBridge {
92    /// The quantum-safe mining wallet
93    wallet: Arc<RwLock<Option<MiningWallet>>>,
94    /// Connection to the Lux consensus ledger
95    ledger: Arc<RwLock<Option<MiningLedger>>>,
96    /// Current account state
97    account: Arc<RwLock<Option<MiningAccount>>>,
98    /// Pending teleport transfers
99    transfers: Arc<RwLock<HashMap<String, TeleportTransfer>>>,
100    /// HTTP client for EVM interactions
101    http_client: reqwest::Client,
102}
103
104impl MiningBridge {
105    /// Create a new mining bridge
106    pub fn new() -> Self {
107        Self {
108            wallet: Arc::new(RwLock::new(None)),
109            ledger: Arc::new(RwLock::new(None)),
110            account: Arc::new(RwLock::new(None)),
111            transfers: Arc::new(RwLock::new(HashMap::new())),
112            http_client: reqwest::Client::new(),
113        }
114    }
115
116    /// Initialize with an existing wallet
117    pub async fn with_wallet(wallet: MiningWallet) -> Result<Self, BridgeError> {
118        let address = wallet.address().to_string();
119        let security_level = wallet.security_level();
120
121        let bridge = Self::new();
122        *bridge.wallet.write().await = Some(wallet);
123        *bridge.account.write().await = Some(MiningAccount::new(address, security_level));
124
125        Ok(bridge)
126    }
127
128    /// Generate a new quantum-safe wallet and initialize the bridge
129    pub async fn generate_new(security_level: SecurityLevel) -> Result<Self, BridgeError> {
130        let wallet = MiningWallet::generate(security_level).await?;
131        Self::with_wallet(wallet).await
132    }
133
134    /// Import wallet from encrypted data
135    pub async fn import_wallet(
136        encrypted_data: &[u8],
137        passphrase: &str,
138    ) -> Result<Self, BridgeError> {
139        let wallet = MiningWallet::import_from_bytes(encrypted_data, passphrase)?;
140        Self::with_wallet(wallet).await
141    }
142
143    /// Connect to the Lux consensus ledger
144    pub async fn connect_ledger(&self, ledger: MiningLedger) -> Result<(), BridgeError> {
145        *self.ledger.write().await = Some(ledger);
146        Ok(())
147    }
148
149    /// Get the wallet address
150    pub async fn address(&self) -> Option<String> {
151        self.wallet.read().await.as_ref().map(|w| w.address().to_string())
152    }
153
154    /// Get the current account state
155    pub async fn account(&self) -> Option<MiningAccount> {
156        self.account.read().await.clone()
157    }
158
159    /// Get wallet's public key for verification
160    pub async fn public_key(&self) -> Option<Vec<u8>> {
161        self.wallet.read().await.as_ref().map(|w| w.public_key().to_vec())
162    }
163
164    /// Register as a miner on the ledger
165    pub async fn register_miner(
166        &self,
167        capabilities: &[u8],
168        stats: &PerformanceStats,
169    ) -> Result<String, BridgeError> {
170        let wallet = self.wallet.read().await;
171        let wallet = wallet.as_ref().ok_or(BridgeError::NoWallet)?;
172
173        let ledger = self.ledger.read().await;
174        let ledger = ledger.as_ref().ok_or(BridgeError::NotConnected)?;
175
176        // Create signature for registration
177        let reg_data = serde_json::json!({
178            "action": "register",
179            "capabilities": hex::encode(capabilities),
180            "stats": stats,
181            "timestamp": current_timestamp(),
182        });
183        let reg_bytes = serde_json::to_vec(&reg_data).unwrap_or_default();
184        let signature = wallet.sign(&reg_bytes).await?;
185
186        // Register on ledger
187        let tx_id = ledger.register_miner(
188            wallet.public_key(),
189            capabilities,
190            stats,
191            &signature,
192        ).await?;
193
194        Ok(tx_id)
195    }
196
197    /// Submit mining proof for rewards
198    pub async fn submit_mining_proof(
199        &self,
200        reward_type: MiningRewardType,
201        proof: &[u8],
202    ) -> Result<String, BridgeError> {
203        let wallet = self.wallet.read().await;
204        let wallet = wallet.as_ref().ok_or(BridgeError::NoWallet)?;
205
206        let ledger = self.ledger.read().await;
207        let ledger = ledger.as_ref().ok_or(BridgeError::NotConnected)?;
208
209        // Sign the proof
210        let signature = wallet.sign(proof).await?;
211
212        // Submit to ledger
213        let tx_id = ledger.submit_proof(
214            wallet.public_key(),
215            reward_type,
216            proof,
217            &signature,
218        ).await?;
219
220        Ok(tx_id)
221    }
222
223    /// Claim pending rewards from the ledger
224    pub async fn claim_rewards(&self, amount: u128) -> Result<String, BridgeError> {
225        let wallet = self.wallet.read().await;
226        let wallet = wallet.as_ref().ok_or(BridgeError::NoWallet)?;
227
228        let ledger = self.ledger.read().await;
229        let ledger = ledger.as_ref().ok_or(BridgeError::NotConnected)?;
230
231        // Get pending rewards from account
232        let pending = {
233            let account = self.account.read().await;
234            account.as_ref().map(|a| a.pending_rewards).unwrap_or(0)
235        };
236
237        if amount > pending {
238            return Err(BridgeError::InsufficientBalance {
239                have: pending,
240                need: amount,
241            });
242        }
243
244        // Create claim data and sign
245        let claim_data = serde_json::json!({
246            "action": "claim",
247            "amount": amount,
248            "recipient": wallet.address(),
249            "timestamp": current_timestamp(),
250        });
251        let claim_bytes = serde_json::to_vec(&claim_data).unwrap_or_default();
252        let signature = wallet.sign(&claim_bytes).await?;
253
254        // Claim from ledger
255        let tx_id = ledger.claim_rewards(
256            wallet.public_key(),
257            amount,
258            wallet.address(),
259            &[], // Empty proof for direct claim
260            &signature,
261        ).await?;
262
263        // Update account state
264        let mut account = self.account.write().await;
265        if let Some(ref mut acc) = *account {
266            acc.pending_rewards = acc.pending_rewards.saturating_sub(amount);
267            acc.claimed_rewards += amount;
268        }
269
270        Ok(tx_id)
271    }
272
273    /// Teleport mining rewards to an EVM chain
274    ///
275    /// This creates a cross-chain transfer from the AI protocol to a supported EVM:
276    /// - Lux C-Chain (96369)
277    /// - Zoo EVM (200200)
278    /// - Hanzo EVM (36963)
279    pub async fn teleport_to_evm(
280        &self,
281        destination: TeleportDestination,
282        amount: u128,
283        recipient: Option<String>,
284    ) -> Result<TeleportTransfer, BridgeError> {
285        let wallet = self.wallet.read().await;
286        let wallet = wallet.as_ref().ok_or(BridgeError::NoWallet)?;
287
288        let ledger = self.ledger.read().await;
289        let ledger = ledger.as_ref().ok_or(BridgeError::NotConnected)?;
290
291        // Check available balance
292        let available = {
293            let account = self.account.read().await;
294            account.as_ref().map(|a| a.available_balance()).unwrap_or(0)
295        };
296
297        if amount > available {
298            return Err(BridgeError::InsufficientBalance {
299                have: available,
300                need: amount,
301            });
302        }
303
304        // Recipient defaults to the wallet's derived EVM address
305        let to_address = recipient.unwrap_or_else(|| {
306            derive_evm_address(wallet.public_key())
307        });
308
309        // Sign the teleport request
310        let teleport_data = serde_json::json!({
311            "action": "teleport_out",
312            "destination": destination.chain_id(),
313            "to_address": to_address,
314            "amount": amount,
315            "timestamp": current_timestamp(),
316        });
317        let teleport_bytes = serde_json::to_vec(&teleport_data).unwrap_or_default();
318        let signature = wallet.sign(&teleport_bytes).await?;
319
320        // Submit to ledger - returns TeleportTransfer directly
321        let transfer = ledger.teleport_out(
322            wallet.public_key(),
323            destination.clone(),
324            &to_address,
325            amount,
326            &signature,
327        ).await?;
328
329        let teleport_id = transfer.teleport_id.clone();
330
331        // Store transfer
332        self.transfers.write().await.insert(teleport_id.clone(), transfer.clone());
333
334        // Update account state
335        let mut account = self.account.write().await;
336        if let Some(ref mut acc) = *account {
337            *acc.teleported_rewards.entry(destination).or_insert(0) += amount;
338            acc.active_transfers.push(teleport_id);
339        }
340
341        Ok(transfer)
342    }
343
344    /// Get teleport transfer status
345    pub async fn get_teleport_status(&self, teleport_id: &str) -> Option<TeleportTransfer> {
346        self.transfers.read().await.get(teleport_id).cloned()
347    }
348
349    /// List all pending teleport transfers
350    pub async fn pending_teleports(&self) -> Vec<TeleportTransfer> {
351        self.transfers.read().await
352            .values()
353            .filter(|t| !matches!(t.status, TeleportStatus::Completed | TeleportStatus::Failed(_)))
354            .cloned()
355            .collect()
356    }
357
358    /// Verify a teleport completed on the destination EVM chain
359    pub async fn verify_teleport_completion(
360        &self,
361        teleport_id: &str,
362    ) -> Result<bool, BridgeError> {
363        let transfer = self.transfers.read().await
364            .get(teleport_id)
365            .cloned()
366            .ok_or_else(|| BridgeError::TeleportFailed("Transfer not found".to_string()))?;
367
368        if matches!(transfer.status, TeleportStatus::Completed) {
369            return Ok(true);
370        }
371
372        // Query the destination EVM chain
373        let evm_client = EvmClient::new(ChainConfig::from_destination(&transfer.destination));
374
375        // Check recipient balance on destination chain
376        match evm_client.get_balance(&transfer.to_address).await {
377            Ok(_balance) => {
378                // Update transfer status
379                let mut transfers = self.transfers.write().await;
380                if let Some(t) = transfers.get_mut(teleport_id) {
381                    t.status = TeleportStatus::Completed;
382                    t.completed_at = Some(current_timestamp());
383                }
384                Ok(true)
385            }
386            Err(e) => Err(BridgeError::Network(e.to_string())),
387        }
388    }
389
390    /// Export wallet for backup
391    pub async fn export_wallet(&self, passphrase: &str) -> Result<Vec<u8>, BridgeError> {
392        let wallet = self.wallet.read().await;
393        let wallet = wallet.as_ref().ok_or(BridgeError::NoWallet)?;
394
395        Ok(wallet.export_to_bytes(passphrase)?)
396    }
397
398    /// Get mining statistics
399    pub async fn get_stats(&self) -> BridgeStats {
400        let account = self.account.read().await;
401        let transfers = self.transfers.read().await;
402
403        let (total_earned, pending, claimed, available) = account
404            .as_ref()
405            .map(|a| (a.total_earned, a.pending_rewards, a.claimed_rewards, a.available_balance()))
406            .unwrap_or((0, 0, 0, 0));
407
408        let teleported: HashMap<String, u128> = account
409            .as_ref()
410            .map(|a| {
411                a.teleported_rewards.iter()
412                    .map(|(k, v)| (format!("{:?}", k), *v))
413                    .collect()
414            })
415            .unwrap_or_default();
416
417        let pending_transfers = transfers.values()
418            .filter(|t| !matches!(t.status, TeleportStatus::Completed | TeleportStatus::Failed(_)))
419            .count();
420
421        BridgeStats {
422            total_earned,
423            pending_rewards: pending,
424            claimed_rewards: claimed,
425            available_balance: available,
426            teleported_by_chain: teleported,
427            pending_teleports: pending_transfers,
428        }
429    }
430}
431
432impl Default for MiningBridge {
433    fn default() -> Self {
434        Self::new()
435    }
436}
437
438/// Bridge statistics
439#[derive(Debug, Clone, Serialize, Deserialize)]
440pub struct BridgeStats {
441    /// Total rewards earned
442    pub total_earned: u128,
443    /// Pending rewards to claim
444    pub pending_rewards: u128,
445    /// Already claimed rewards
446    pub claimed_rewards: u128,
447    /// Available to teleport
448    pub available_balance: u128,
449    /// Teleported by destination chain
450    pub teleported_by_chain: HashMap<String, u128>,
451    /// Number of pending teleport transfers
452    pub pending_teleports: usize,
453}
454
455// Helper to add from_destination to ChainConfig
456impl ChainConfig {
457    pub fn from_destination(dest: &TeleportDestination) -> Self {
458        match dest {
459            TeleportDestination::LuxCChain => Self::lux_mainnet(),
460            TeleportDestination::ZooEvm => Self::zoo_mainnet(),
461            TeleportDestination::HanzoEvm => Self::hanzo_mainnet(),
462        }
463    }
464}
465
466/// Get current timestamp
467fn current_timestamp() -> u64 {
468    std::time::SystemTime::now()
469        .duration_since(std::time::UNIX_EPOCH)
470        .unwrap_or_default()
471        .as_secs()
472}
473
474/// Derive an EVM-compatible address from a quantum-safe public key
475fn derive_evm_address(public_key: &[u8]) -> String {
476    // Hash the public key and take last 20 bytes
477    let hash = blake3::hash(public_key);
478    let bytes = hash.as_bytes();
479    format!("0x{}", hex::encode(&bytes[12..32]))
480}
481
482#[cfg(test)]
483mod tests {
484    use super::*;
485
486    #[tokio::test]
487    async fn test_bridge_creation() {
488        let bridge = MiningBridge::generate_new(SecurityLevel::Level3).await.unwrap();
489
490        let address = bridge.address().await;
491        assert!(address.is_some());
492        assert!(address.unwrap().starts_with("0x"));
493    }
494
495    #[tokio::test]
496    async fn test_bridge_stats() {
497        let bridge = MiningBridge::generate_new(SecurityLevel::Level3).await.unwrap();
498
499        let stats = bridge.get_stats().await;
500        assert_eq!(stats.total_earned, 0);
501        assert_eq!(stats.pending_rewards, 0);
502        assert_eq!(stats.available_balance, 0);
503    }
504
505    #[tokio::test]
506    async fn test_wallet_export_import() {
507        let bridge = MiningBridge::generate_new(SecurityLevel::Level3).await.unwrap();
508
509        let original_address = bridge.address().await.unwrap();
510
511        // Export wallet
512        let passphrase = "test_passphrase_123";
513        let encrypted = bridge.export_wallet(passphrase).await.unwrap();
514
515        // Import into new bridge
516        let imported_bridge = MiningBridge::import_wallet(&encrypted, passphrase).await.unwrap();
517        let imported_address = imported_bridge.address().await.unwrap();
518
519        assert_eq!(original_address, imported_address);
520    }
521
522    #[test]
523    fn test_derive_evm_address() {
524        let public_key = vec![0u8; 1312]; // ML-DSA-65 public key size
525        let address = derive_evm_address(&public_key);
526
527        assert!(address.starts_with("0x"));
528        assert_eq!(address.len(), 42); // 0x + 40 hex chars
529    }
530
531    #[tokio::test]
532    async fn test_no_wallet_error() {
533        let bridge = MiningBridge::new();
534
535        let result = bridge.claim_rewards(100).await;
536        assert!(matches!(result, Err(BridgeError::NoWallet)));
537    }
538
539    #[tokio::test]
540    async fn test_mining_account() {
541        let account = MiningAccount::new(
542            "HS_test_address".to_string(),
543            SecurityLevel::Level3
544        );
545
546        assert_eq!(account.total_earned, 0);
547        assert_eq!(account.available_balance(), 0);
548        assert!(account.teleported_rewards.is_empty());
549    }
550}