1use 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#[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#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct MiningAccount {
50 pub address: String,
52 pub security_level: SecurityLevel,
54 pub total_earned: u128,
56 pub pending_rewards: u128,
58 pub claimed_rewards: u128,
60 pub teleported_rewards: HashMap<TeleportDestination, u128>,
62 pub active_transfers: Vec<String>,
64 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 pub fn available_balance(&self) -> u128 {
84 self.claimed_rewards.saturating_sub(
85 self.teleported_rewards.values().sum::<u128>()
86 )
87 }
88}
89
90pub struct MiningBridge {
92 wallet: Arc<RwLock<Option<MiningWallet>>>,
94 ledger: Arc<RwLock<Option<MiningLedger>>>,
96 account: Arc<RwLock<Option<MiningAccount>>>,
98 transfers: Arc<RwLock<HashMap<String, TeleportTransfer>>>,
100 http_client: reqwest::Client,
102}
103
104impl MiningBridge {
105 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 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 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 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 pub async fn connect_ledger(&self, ledger: MiningLedger) -> Result<(), BridgeError> {
145 *self.ledger.write().await = Some(ledger);
146 Ok(())
147 }
148
149 pub async fn address(&self) -> Option<String> {
151 self.wallet.read().await.as_ref().map(|w| w.address().to_string())
152 }
153
154 pub async fn account(&self) -> Option<MiningAccount> {
156 self.account.read().await.clone()
157 }
158
159 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 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 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(®_data).unwrap_or_default();
184 let signature = wallet.sign(®_bytes).await?;
185
186 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 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 let signature = wallet.sign(proof).await?;
211
212 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 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 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 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 let tx_id = ledger.claim_rewards(
256 wallet.public_key(),
257 amount,
258 wallet.address(),
259 &[], &signature,
261 ).await?;
262
263 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 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 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 let to_address = recipient.unwrap_or_else(|| {
306 derive_evm_address(wallet.public_key())
307 });
308
309 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 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 self.transfers.write().await.insert(teleport_id.clone(), transfer.clone());
333
334 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 pub async fn get_teleport_status(&self, teleport_id: &str) -> Option<TeleportTransfer> {
346 self.transfers.read().await.get(teleport_id).cloned()
347 }
348
349 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 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 let evm_client = EvmClient::new(ChainConfig::from_destination(&transfer.destination));
374
375 match evm_client.get_balance(&transfer.to_address).await {
377 Ok(_balance) => {
378 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 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 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#[derive(Debug, Clone, Serialize, Deserialize)]
440pub struct BridgeStats {
441 pub total_earned: u128,
443 pub pending_rewards: u128,
445 pub claimed_rewards: u128,
447 pub available_balance: u128,
449 pub teleported_by_chain: HashMap<String, u128>,
451 pub pending_teleports: usize,
453}
454
455impl 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
466fn current_timestamp() -> u64 {
468 std::time::SystemTime::now()
469 .duration_since(std::time::UNIX_EPOCH)
470 .unwrap_or_default()
471 .as_secs()
472}
473
474fn derive_evm_address(public_key: &[u8]) -> String {
476 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 let passphrase = "test_passphrase_123";
513 let encrypted = bridge.export_wallet(passphrase).await.unwrap();
514
515 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]; let address = derive_evm_address(&public_key);
526
527 assert!(address.starts_with("0x"));
528 assert_eq!(address.len(), 42); }
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}