kaccy_bitcoin/
transaction_manager.rs

1//! Transaction Manager Integration Module
2//!
3//! This module provides a high-level interface that integrates UTXO management,
4//! transaction limits, and suspicious activity detection into a single cohesive API.
5
6use std::sync::Arc;
7use tokio::sync::RwLock;
8
9use crate::activity_monitor::{
10    ActivityMonitor, ActivityMonitorConfig, SuspiciousActivity, TransactionRecord,
11};
12use crate::client::BitcoinClient;
13use crate::error::Result;
14use crate::tx_limits::{LimitConfig, LimitEnforcer};
15use crate::utxo::{
16    ConsolidationConfig, ConsolidationPlan, SelectionStrategy, UtxoManager, UtxoSelection,
17};
18
19/// Transaction manager configuration
20#[derive(Debug, Clone)]
21pub struct TransactionManagerConfig {
22    /// UTXO consolidation config
23    pub consolidation: ConsolidationConfig,
24    /// Transaction limits config
25    pub limits: LimitConfig,
26    /// Activity monitoring config
27    pub activity: ActivityMonitorConfig,
28    /// Enable automatic UTXO consolidation
29    pub auto_consolidate: bool,
30}
31
32impl Default for TransactionManagerConfig {
33    fn default() -> Self {
34        Self {
35            consolidation: ConsolidationConfig::default(),
36            limits: LimitConfig::default(),
37            activity: ActivityMonitorConfig::default(),
38            auto_consolidate: true,
39        }
40    }
41}
42
43/// Integrated transaction manager
44pub struct TransactionManager {
45    utxo_manager: UtxoManager,
46    limit_enforcer: Arc<RwLock<LimitEnforcer>>,
47    activity_monitor: Arc<ActivityMonitor>,
48    config: TransactionManagerConfig,
49}
50
51/// Transaction validation result
52#[derive(Debug, Clone)]
53pub struct TransactionValidation {
54    /// Whether the transaction is allowed
55    pub allowed: bool,
56    /// Reason for rejection (if not allowed)
57    pub rejection_reason: Option<String>,
58    /// Suspicious activity detected (if any)
59    pub suspicious_activity: Option<SuspiciousActivity>,
60    /// UTXO selection (if requested)
61    pub utxo_selection: Option<UtxoSelection>,
62}
63
64/// Consolidated transaction statistics
65#[derive(Debug, Clone)]
66pub struct TransactionStats {
67    /// Total UTXOs available
68    pub total_utxos: usize,
69    /// Total balance in satoshis
70    pub total_balance_sats: u64,
71    /// Number of spendable UTXOs
72    pub spendable_utxos: usize,
73    /// Whether consolidation is recommended
74    pub consolidation_recommended: bool,
75    /// Consolidation plan (if available)
76    pub consolidation_plan: Option<ConsolidationPlan>,
77}
78
79impl TransactionManager {
80    /// Create a new transaction manager
81    pub fn new(client: Arc<BitcoinClient>, config: TransactionManagerConfig) -> Self {
82        let utxo_manager = UtxoManager::new(client, config.consolidation.clone());
83        let limit_enforcer = Arc::new(RwLock::new(LimitEnforcer::new(config.limits.clone())));
84        let activity_monitor = Arc::new(ActivityMonitor::new(config.activity.clone()));
85
86        Self {
87            utxo_manager,
88            limit_enforcer,
89            activity_monitor,
90            config,
91        }
92    }
93
94    /// Validate and prepare a transaction
95    pub async fn validate_transaction(
96        &self,
97        user_id: &str,
98        amount_sats: u64,
99        fee_rate: f64,
100        txid: Option<String>,
101    ) -> Result<TransactionValidation> {
102        // Check transaction limits
103        let limit_check = {
104            let enforcer = self.limit_enforcer.read().await;
105            enforcer.check_transaction(user_id, amount_sats).await
106        };
107
108        if let Err(e) = limit_check {
109            return Ok(TransactionValidation {
110                allowed: false,
111                rejection_reason: Some(format!("Limit exceeded: {}", e)),
112                suspicious_activity: None,
113                utxo_selection: None,
114            });
115        }
116
117        // Check for suspicious activity
118        let activity_record = TransactionRecord {
119            txid: txid.clone().unwrap_or_else(|| "pending".to_string()),
120            user_id: user_id.to_string(),
121            amount_sats,
122            timestamp: chrono::Utc::now(),
123        };
124
125        let suspicious = self
126            .activity_monitor
127            .record_transaction(activity_record)
128            .await;
129
130        // Select UTXOs if amount is specified
131        let utxo_selection = if amount_sats > 0 {
132            match self.utxo_manager.select_utxos(
133                amount_sats,
134                fee_rate,
135                SelectionStrategy::BranchAndBound,
136            ) {
137                Ok(selection) => Some(selection),
138                Err(e) => {
139                    return Ok(TransactionValidation {
140                        allowed: false,
141                        rejection_reason: Some(format!("UTXO selection failed: {}", e)),
142                        suspicious_activity: suspicious,
143                        utxo_selection: None,
144                    });
145                }
146            }
147        } else {
148            None
149        };
150
151        Ok(TransactionValidation {
152            allowed: true,
153            rejection_reason: None,
154            suspicious_activity: suspicious,
155            utxo_selection,
156        })
157    }
158
159    /// Record a completed transaction
160    pub async fn record_completed_transaction(
161        &self,
162        user_id: &str,
163        amount_sats: u64,
164        txid: String,
165    ) {
166        let enforcer = self.limit_enforcer.write().await;
167        enforcer
168            .record_transaction(user_id, amount_sats, Some(txid))
169            .await;
170    }
171
172    /// Get transaction statistics
173    pub fn get_statistics(&self) -> Result<TransactionStats> {
174        let utxos = self.utxo_manager.list_utxos()?;
175
176        let total_balance_sats: u64 = utxos.iter().map(|u| u.amount_sats).sum();
177        let spendable_utxos = utxos.iter().filter(|u| u.spendable && u.safe).count();
178
179        let consolidation_recommended = self.utxo_manager.should_consolidate()?;
180        let consolidation_plan = if consolidation_recommended {
181            self.utxo_manager.get_consolidation_plan()?
182        } else {
183            None
184        };
185
186        Ok(TransactionStats {
187            total_utxos: utxos.len(),
188            total_balance_sats,
189            spendable_utxos,
190            consolidation_recommended,
191            consolidation_plan,
192        })
193    }
194
195    /// Get user usage statistics for a period
196    pub async fn get_user_usage(
197        &self,
198        user_id: &str,
199        period: crate::tx_limits::LimitPeriod,
200    ) -> (u64, u32) {
201        let enforcer = self.limit_enforcer.read().await;
202        enforcer.get_user_usage(user_id, period).await
203    }
204
205    /// Get platform usage statistics for a period
206    pub async fn get_platform_usage(&self, period: crate::tx_limits::LimitPeriod) -> (u64, u32) {
207        let enforcer = self.limit_enforcer.read().await;
208        enforcer.get_platform_usage(period).await
209    }
210
211    /// Subscribe to suspicious activity alerts
212    pub fn subscribe_to_alerts(&self) -> tokio::sync::broadcast::Receiver<SuspiciousActivity> {
213        self.activity_monitor.subscribe()
214    }
215
216    /// Check if consolidation should be performed
217    pub async fn should_consolidate_now(&self) -> Result<bool> {
218        if !self.config.auto_consolidate {
219            return Ok(false);
220        }
221
222        let stats = self.get_statistics()?;
223        Ok(stats.consolidation_recommended)
224    }
225
226    /// Get consolidation plan
227    pub fn get_consolidation_plan(&self) -> Result<Option<ConsolidationPlan>> {
228        self.utxo_manager.get_consolidation_plan()
229    }
230
231    /// Get activity monitor reference
232    pub fn activity_monitor(&self) -> &ActivityMonitor {
233        &self.activity_monitor
234    }
235
236    /// Get UTXO manager reference
237    pub fn utxo_manager(&self) -> &UtxoManager {
238        &self.utxo_manager
239    }
240}
241
242#[cfg(test)]
243mod tests {
244    use super::*;
245
246    #[test]
247    fn test_transaction_manager_config_defaults() {
248        let config = TransactionManagerConfig::default();
249        assert!(config.auto_consolidate);
250        assert_eq!(config.consolidation.min_utxo_count, 50);
251        assert_eq!(config.limits.single_tx_max_sats, 50_000_000);
252        assert_eq!(config.activity.large_tx_threshold_sats, 10_000_000);
253    }
254
255    #[test]
256    fn test_transaction_validation_result() {
257        let validation = TransactionValidation {
258            allowed: true,
259            rejection_reason: None,
260            suspicious_activity: None,
261            utxo_selection: None,
262        };
263
264        assert!(validation.allowed);
265        assert!(validation.rejection_reason.is_none());
266    }
267}