| 1 |
//! Contribution-Based Economic Manager |
| 2 |
//! |
| 3 |
//! Replaces token-based economics with contribution tracking and cooperative resource allocation |
| 4 |
|
| 5 |
use anyhow::Result; |
| 6 |
use serde::{Deserialize, Serialize}; |
| 7 |
use std::collections::HashMap; |
| 8 |
use chrono::{DateTime, Utc, Duration}; |
| 9 |
|
| 10 |
use super::contribution_tracker::{ContributionTracker, UserContribution, PriorityLevel, AccountStatus}; |
| 11 |
|
| 12 |
/// Main economic manager using contribution-based model |
| 13 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 14 |
pub struct ContributionEconomicManager { |
| 15 |
/// Contribution tracking system |
| 16 |
pub contribution_tracker: ContributionTracker, |
| 17 |
/// Resource allocation queue |
| 18 |
pub allocation_queue: AllocationQueue, |
| 19 |
/// Network resource management |
| 20 |
pub resource_manager: ResourceManager, |
| 21 |
/// Simple referral tracking |
| 22 |
pub referral_tracker: SimpleReferralTracker, |
| 23 |
} |
| 24 |
|
| 25 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 26 |
pub struct AllocationQueue { |
| 27 |
/// Pending storage requests ordered by priority |
| 28 |
pub storage_requests: Vec<StorageRequest>, |
| 29 |
/// Active storage allocations |
| 30 |
pub active_allocations: HashMap<String, StorageAllocation>, |
| 31 |
/// Allocation history for analytics |
| 32 |
pub allocation_history: Vec<AllocationRecord>, |
| 33 |
} |
| 34 |
|
| 35 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 36 |
pub struct StorageRequest { |
| 37 |
pub request_id: String, |
| 38 |
pub user_id: String, |
| 39 |
pub requested_gb: u64, |
| 40 |
pub priority_score: u32, |
| 41 |
pub requested_at: DateTime<Utc>, |
| 42 |
pub expires_at: DateTime<Utc>, |
| 43 |
pub requirements: StorageRequirements, |
| 44 |
} |
| 45 |
|
| 46 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 47 |
pub struct StorageRequirements { |
| 48 |
pub durability_level: DurabilityLevel, |
| 49 |
pub access_frequency: AccessFrequency, |
| 50 |
pub geographic_preference: Option<String>, |
| 51 |
pub max_latency_ms: Option<u64>, |
| 52 |
} |
| 53 |
|
| 54 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 55 |
pub enum DurabilityLevel { |
| 56 |
/// Standard redundancy (3 copies) |
| 57 |
Standard, |
| 58 |
/// High redundancy (5 copies) |
| 59 |
High, |
| 60 |
/// Critical redundancy (7 copies) |
| 61 |
Critical, |
| 62 |
} |
| 63 |
|
| 64 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 65 |
pub enum AccessFrequency { |
| 66 |
/// Rarely accessed, optimize for cost |
| 67 |
Cold, |
| 68 |
/// Occasionally accessed |
| 69 |
Warm, |
| 70 |
/// Frequently accessed, optimize for speed |
| 71 |
Hot, |
| 72 |
} |
| 73 |
|
| 74 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 75 |
pub struct StorageAllocation { |
| 76 |
pub allocation_id: String, |
| 77 |
pub user_id: String, |
| 78 |
pub allocated_gb: u64, |
| 79 |
pub allocated_to_nodes: Vec<String>, |
| 80 |
pub allocated_at: DateTime<Utc>, |
| 81 |
pub expires_at: Option<DateTime<Utc>>, |
| 82 |
pub status: AllocationStatus, |
| 83 |
} |
| 84 |
|
| 85 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 86 |
pub enum AllocationStatus { |
| 87 |
Active, |
| 88 |
Expired, |
| 89 |
Revoked, |
| 90 |
} |
| 91 |
|
| 92 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 93 |
pub struct AllocationRecord { |
| 94 |
pub user_id: String, |
| 95 |
pub action: AllocationAction, |
| 96 |
pub amount_gb: u64, |
| 97 |
pub reason: String, |
| 98 |
pub timestamp: DateTime<Utc>, |
| 99 |
} |
| 100 |
|
| 101 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 102 |
pub enum AllocationAction { |
| 103 |
Granted, |
| 104 |
Denied, |
| 105 |
Revoked, |
| 106 |
Expired, |
| 107 |
} |
| 108 |
|
| 109 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 110 |
pub struct ResourceManager { |
| 111 |
/// Available storage capacity per node |
| 112 |
pub node_capacity: HashMap<String, NodeCapacity>, |
| 113 |
/// Current utilization statistics |
| 114 |
pub utilization_stats: UtilizationStats, |
| 115 |
/// Resource allocation policies |
| 116 |
pub allocation_policies: AllocationPolicies, |
| 117 |
} |
| 118 |
|
| 119 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 120 |
pub struct NodeCapacity { |
| 121 |
pub node_id: String, |
| 122 |
pub total_capacity_gb: u64, |
| 123 |
pub available_capacity_gb: u64, |
| 124 |
pub allocated_capacity_gb: u64, |
| 125 |
pub reliability_score: f64, |
| 126 |
pub performance_metrics: NodePerformance, |
| 127 |
pub last_updated: DateTime<Utc>, |
| 128 |
} |
| 129 |
|
| 130 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 131 |
pub struct NodePerformance { |
| 132 |
pub uptime_percentage: f64, |
| 133 |
pub average_response_time_ms: u64, |
| 134 |
pub bandwidth_mbps: f64, |
| 135 |
pub success_rate: f64, |
| 136 |
} |
| 137 |
|
| 138 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 139 |
pub struct UtilizationStats { |
| 140 |
pub total_network_capacity_gb: u64, |
| 141 |
pub total_allocated_gb: u64, |
| 142 |
pub total_available_gb: u64, |
| 143 |
pub utilization_percentage: f64, |
| 144 |
pub active_nodes: u32, |
| 145 |
pub last_updated: DateTime<Utc>, |
| 146 |
} |
| 147 |
|
| 148 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 149 |
pub struct AllocationPolicies { |
| 150 |
/// Maximum allocation per user based on contribution level |
| 151 |
pub max_allocation_ratios: HashMap<PriorityLevel, f64>, |
| 152 |
/// Minimum contribution score for allocation |
| 153 |
pub min_contribution_score: f64, |
| 154 |
/// Grace period for new users (days) |
| 155 |
pub new_user_grace_period: u32, |
| 156 |
/// Allocation request timeout (hours) |
| 157 |
pub request_timeout_hours: u32, |
| 158 |
} |
| 159 |
|
| 160 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 161 |
pub struct SimpleReferralTracker { |
| 162 |
/// User referrals (referrer -> list of referred users) |
| 163 |
pub referrals: HashMap<String, Vec<ReferralRecord>>, |
| 164 |
/// Referral bonuses awarded |
| 165 |
pub bonuses: HashMap<String, Vec<ReferralBonus>>, |
| 166 |
/// Configuration |
| 167 |
pub config: ReferralConfig, |
| 168 |
} |
| 169 |
|
| 170 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 171 |
pub struct ReferralRecord { |
| 172 |
pub referred_user_id: String, |
| 173 |
pub referred_at: DateTime<Utc>, |
| 174 |
pub bonus_awarded: bool, |
| 175 |
pub bonus_awarded_at: Option<DateTime<Utc>>, |
| 176 |
} |
| 177 |
|
| 178 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 179 |
pub struct ReferralBonus { |
| 180 |
pub bonus_gb: u64, |
| 181 |
pub reason: String, |
| 182 |
pub awarded_at: DateTime<Utc>, |
| 183 |
pub expires_at: Option<DateTime<Utc>>, |
| 184 |
} |
| 185 |
|
| 186 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 187 |
pub struct ReferralConfig { |
| 188 |
/// Storage bonus for successful referral (GB) |
| 189 |
pub referral_bonus_gb: u64, |
| 190 |
/// Minimum contribution from referee before bonus |
| 191 |
pub min_referee_contribution_gb: u64, |
| 192 |
/// Maximum number of referral bonuses per user |
| 193 |
pub max_referral_bonuses: u32, |
| 194 |
/// Bonus expiration time (days) |
| 195 |
pub bonus_expires_days: u32, |
| 196 |
} |
| 197 |
|
| 198 |
impl ContributionEconomicManager { |
| 199 |
pub fn new() -> Self { |
| 200 |
Self { |
| 201 |
contribution_tracker: ContributionTracker::new(), |
| 202 |
allocation_queue: AllocationQueue { |
| 203 |
storage_requests: Vec::new(), |
| 204 |
active_allocations: HashMap::new(), |
| 205 |
allocation_history: Vec::new(), |
| 206 |
}, |
| 207 |
resource_manager: ResourceManager { |
| 208 |
node_capacity: HashMap::new(), |
| 209 |
utilization_stats: UtilizationStats { |
| 210 |
total_network_capacity_gb: 0, |
| 211 |
total_allocated_gb: 0, |
| 212 |
total_available_gb: 0, |
| 213 |
utilization_percentage: 0.0, |
| 214 |
active_nodes: 0, |
| 215 |
last_updated: Utc::now(), |
| 216 |
}, |
| 217 |
allocation_policies: AllocationPolicies { |
| 218 |
max_allocation_ratios: { |
| 219 |
let mut ratios = HashMap::new(); |
| 220 |
ratios.insert(PriorityLevel::Deficit, 0.5); // Can use 50% of what they offer |
| 221 |
ratios.insert(PriorityLevel::Balanced, 1.0); // Can use 100% of what they offer |
| 222 |
ratios.insert(PriorityLevel::Surplus, 1.5); // Can use 150% of what they offer |
| 223 |
ratios.insert(PriorityLevel::Generous, 2.0); // Can use 200% of what they offer |
| 224 |
ratios |
| 225 |
}, |
| 226 |
min_contribution_score: 0.5, |
| 227 |
new_user_grace_period: 30, |
| 228 |
request_timeout_hours: 24, |
| 229 |
}, |
| 230 |
}, |
| 231 |
referral_tracker: SimpleReferralTracker { |
| 232 |
referrals: HashMap::new(), |
| 233 |
bonuses: HashMap::new(), |
| 234 |
config: ReferralConfig { |
| 235 |
referral_bonus_gb: 10, |
| 236 |
min_referee_contribution_gb: 100, |
| 237 |
max_referral_bonuses: 10, |
| 238 |
bonus_expires_days: 365, |
| 239 |
}, |
| 240 |
}, |
| 241 |
} |
| 242 |
} |
| 243 |
|
| 244 |
/// Register a new node offering storage |
| 245 |
pub async fn register_node(&mut self, user_id: String, storage_gb: u64, performance: NodePerformance) -> Result<()> { |
| 246 |
// Register in contribution tracker |
| 247 |
self.contribution_tracker.register_user(user_id.clone(), storage_gb).await?; |
| 248 |
|
| 249 |
// Add node capacity |
| 250 |
self.resource_manager.node_capacity.insert(user_id.clone(), NodeCapacity { |
| 251 |
node_id: user_id, |
| 252 |
total_capacity_gb: storage_gb, |
| 253 |
available_capacity_gb: storage_gb, |
| 254 |
allocated_capacity_gb: 0, |
| 255 |
reliability_score: 1.0, // Start with neutral score |
| 256 |
performance_metrics: performance, |
| 257 |
last_updated: Utc::now(), |
| 258 |
}); |
| 259 |
|
| 260 |
self.update_utilization_stats().await?; |
| 261 |
Ok(()) |
| 262 |
} |
| 263 |
|
| 264 |
/// Request storage allocation |
| 265 |
pub async fn request_storage(&mut self, user_id: String, requested_gb: u64, requirements: StorageRequirements) -> Result<String> { |
| 266 |
// Check if user can make this request |
| 267 |
if !self.contribution_tracker.can_request_storage(&user_id, requested_gb)? { |
| 268 |
return Err(anyhow::anyhow!("Request denied: insufficient contribution or account status")); |
| 269 |
} |
| 270 |
|
| 271 |
// Get priority score |
| 272 |
let priority_score = self.contribution_tracker.get_allocation_priority(&user_id)?; |
| 273 |
|
| 274 |
// Create storage request |
| 275 |
let request_id = format!("req_{}", uuid::Uuid::new_v4()); |
| 276 |
let request = StorageRequest { |
| 277 |
request_id: request_id.clone(), |
| 278 |
user_id: user_id.clone(), |
| 279 |
requested_gb, |
| 280 |
priority_score, |
| 281 |
requested_at: Utc::now(), |
| 282 |
expires_at: Utc::now() + Duration::hours(self.resource_manager.allocation_policies.request_timeout_hours as i64), |
| 283 |
requirements, |
| 284 |
}; |
| 285 |
|
| 286 |
// Insert request in priority order |
| 287 |
self.allocation_queue.storage_requests.push(request); |
| 288 |
self.allocation_queue.storage_requests.sort_by(|a, b| b.priority_score.cmp(&a.priority_score)); |
| 289 |
|
| 290 |
// Try to process immediately |
| 291 |
self.process_allocation_queue().await?; |
| 292 |
|
| 293 |
Ok(request_id) |
| 294 |
} |
| 295 |
|
| 296 |
/// Process pending storage allocations |
| 297 |
pub async fn process_allocation_queue(&mut self) -> Result<()> { |
| 298 |
let mut processed_requests = Vec::new(); |
| 299 |
let mut allocations_to_record = Vec::new(); |
| 300 |
let mut new_allocations = Vec::new(); |
| 301 |
|
| 302 |
// First pass: collect requests to process without borrowing conflicts |
| 303 |
let requests_to_process: Vec<(usize, crate::allocation::AllocationRequest)> = self.allocation_queue.storage_requests |
| 304 |
.iter() |
| 305 |
.enumerate() |
| 306 |
.map(|(i, req)| (i, req.clone())) |
| 307 |
.collect(); |
| 308 |
|
| 309 |
for (i, request) in requests_to_process { |
| 310 |
// Check if request expired |
| 311 |
if request.expires_at < Utc::now() { |
| 312 |
allocations_to_record.push((request.user_id.clone(), AllocationAction::Expired, request.requested_gb, "Request expired".to_string())); |
| 313 |
processed_requests.push(i); |
| 314 |
continue; |
| 315 |
} |
| 316 |
|
| 317 |
// Try to allocate storage |
| 318 |
match self.allocate_storage_for_request(&request).await { |
| 319 |
Ok(allocation) => { |
| 320 |
new_allocations.push(allocation.clone()); |
| 321 |
allocations_to_record.push((request.user_id.clone(), AllocationAction::Granted, request.requested_gb, "Storage allocated successfully".to_string())); |
| 322 |
processed_requests.push(i); |
| 323 |
}, |
| 324 |
Err(_) => { |
| 325 |
// Cannot allocate right now, leave in queue |
| 326 |
} |
| 327 |
} |
| 328 |
} |
| 329 |
|
| 330 |
// Record all allocations |
| 331 |
for (user_id, action, gb, message) in allocations_to_record { |
| 332 |
self.record_allocation(user_id, action, gb, message).await; |
| 333 |
} |
| 334 |
|
| 335 |
// Add new allocations |
| 336 |
for allocation in new_allocations { |
| 337 |
self.allocation_queue.active_allocations.insert(allocation.allocation_id.clone(), allocation); |
| 338 |
} |
| 339 |
|
| 340 |
// Remove processed requests (in reverse order to maintain indices) |
| 341 |
for &i in processed_requests.iter().rev() { |
| 342 |
self.allocation_queue.storage_requests.remove(i); |
| 343 |
} |
| 344 |
|
| 345 |
Ok(()) |
| 346 |
} |
| 347 |
|
| 348 |
/// Allocate storage for a specific request |
| 349 |
async fn allocate_storage_for_request(&mut self, request: &StorageRequest) -> Result<StorageAllocation> { |
| 350 |
// Find suitable nodes based on requirements |
| 351 |
let suitable_nodes = self.find_suitable_nodes(&request.requirements, request.requested_gb)?; |
| 352 |
|
| 353 |
if suitable_nodes.is_empty() { |
| 354 |
return Err(anyhow::anyhow!("No suitable nodes available")); |
| 355 |
} |
| 356 |
|
| 357 |
// Update user's storage usage |
| 358 |
self.contribution_tracker.update_storage_usage(&request.user_id, |
| 359 |
self.contribution_tracker.get_user_status(&request.user_id).unwrap().storage_used_gb + request.requested_gb).await?; |
| 360 |
|
| 361 |
// Create allocation |
| 362 |
let allocation = StorageAllocation { |
| 363 |
allocation_id: format!("alloc_{}", uuid::Uuid::new_v4()), |
| 364 |
user_id: request.user_id.clone(), |
| 365 |
allocated_gb: request.requested_gb, |
| 366 |
allocated_to_nodes: suitable_nodes, |
| 367 |
allocated_at: Utc::now(), |
| 368 |
expires_at: None, // No expiration for now |
| 369 |
status: AllocationStatus::Active, |
| 370 |
}; |
| 371 |
|
| 372 |
Ok(allocation) |
| 373 |
} |
| 374 |
|
| 375 |
/// Find suitable nodes for storage requirements |
| 376 |
fn find_suitable_nodes(&self, requirements: &StorageRequirements, needed_gb: u64) -> Result<Vec<String>> { |
| 377 |
let mut suitable_nodes: Vec<_> = self.resource_manager.node_capacity.values() |
| 378 |
.filter(|node| { |
| 379 |
node.available_capacity_gb >= needed_gb && |
| 380 |
node.reliability_score >= 0.8 && |
| 381 |
node.performance_metrics.uptime_percentage >= 95.0 |
| 382 |
}) |
| 383 |
.collect(); |
| 384 |
|
| 385 |
// Sort by reliability and performance |
| 386 |
suitable_nodes.sort_by(|a, b| { |
| 387 |
b.reliability_score.partial_cmp(&a.reliability_score).unwrap() |
| 388 |
.then_with(|| b.performance_metrics.uptime_percentage.partial_cmp(&a.performance_metrics.uptime_percentage).unwrap()) |
| 389 |
}); |
| 390 |
|
| 391 |
// Return top nodes (could implement more sophisticated selection based on requirements) |
| 392 |
let num_nodes = match requirements.durability_level { |
| 393 |
DurabilityLevel::Standard => 3, |
| 394 |
DurabilityLevel::High => 5, |
| 395 |
DurabilityLevel::Critical => 7, |
| 396 |
}; |
| 397 |
|
| 398 |
Ok(suitable_nodes.iter() |
| 399 |
.take(num_nodes.min(suitable_nodes.len())) |
| 400 |
.map(|node| node.node_id.clone()) |
| 401 |
.collect()) |
| 402 |
} |
| 403 |
|
| 404 |
/// Record allocation action in history |
| 405 |
async fn record_allocation(&mut self, user_id: String, action: AllocationAction, amount_gb: u64, reason: String) { |
| 406 |
let record = AllocationRecord { |
| 407 |
user_id, |
| 408 |
action, |
| 409 |
amount_gb, |
| 410 |
reason, |
| 411 |
timestamp: Utc::now(), |
| 412 |
}; |
| 413 |
self.allocation_queue.allocation_history.push(record); |
| 414 |
} |
| 415 |
|
| 416 |
/// Update network utilization statistics |
| 417 |
async fn update_utilization_stats(&mut self) -> Result<()> { |
| 418 |
let mut total_capacity = 0u64; |
| 419 |
let mut total_allocated = 0u64; |
| 420 |
let mut active_nodes = 0u32; |
| 421 |
|
| 422 |
for capacity in self.resource_manager.node_capacity.values() { |
| 423 |
total_capacity += capacity.total_capacity_gb; |
| 424 |
total_allocated += capacity.allocated_capacity_gb; |
| 425 |
if capacity.available_capacity_gb > 0 { |
| 426 |
active_nodes += 1; |
| 427 |
} |
| 428 |
} |
| 429 |
|
| 430 |
self.resource_manager.utilization_stats = UtilizationStats { |
| 431 |
total_network_capacity_gb: total_capacity, |
| 432 |
total_allocated_gb: total_allocated, |
| 433 |
total_available_gb: total_capacity - total_allocated, |
| 434 |
utilization_percentage: if total_capacity > 0 { |
| 435 |
(total_allocated as f64 / total_capacity as f64) * 100.0 |
| 436 |
} else { |
| 437 |
0.0 |
| 438 |
}, |
| 439 |
active_nodes, |
| 440 |
last_updated: Utc::now(), |
| 441 |
}; |
| 442 |
|
| 443 |
Ok(()) |
| 444 |
} |
| 445 |
|
| 446 |
/// Simple referral system - award bonus for successful referral |
| 447 |
pub async fn process_referral(&mut self, referrer_id: String, referred_user_id: String) -> Result<()> { |
| 448 |
// Check if referred user meets minimum contribution |
| 449 |
let referred_contribution = self.contribution_tracker.get_user_status(&referred_user_id) |
| 450 |
.ok_or_else(|| anyhow::anyhow!("Referred user not found"))?; |
| 451 |
|
| 452 |
if referred_contribution.storage_offered_gb >= self.referral_tracker.config.min_referee_contribution_gb { |
| 453 |
// Award bonus to both users |
| 454 |
let bonus = ReferralBonus { |
| 455 |
bonus_gb: self.referral_tracker.config.referral_bonus_gb, |
| 456 |
reason: format!("Referral bonus for {}", referred_user_id), |
| 457 |
awarded_at: Utc::now(), |
| 458 |
expires_at: Some(Utc::now() + Duration::days(self.referral_tracker.config.bonus_expires_days as i64)), |
| 459 |
}; |
| 460 |
|
| 461 |
// Add bonus storage allocation for referrer |
| 462 |
self.referral_tracker.bonuses.entry(referrer_id.clone()).or_insert_with(Vec::new).push(bonus.clone()); |
| 463 |
self.referral_tracker.bonuses.entry(referred_user_id.clone()).or_insert_with(Vec::new).push(bonus); |
| 464 |
|
| 465 |
// Record the referral |
| 466 |
let referral_record = ReferralRecord { |
| 467 |
referred_user_id: referred_user_id.clone(), |
| 468 |
referred_at: Utc::now(), |
| 469 |
bonus_awarded: true, |
| 470 |
bonus_awarded_at: Some(Utc::now()), |
| 471 |
}; |
| 472 |
self.referral_tracker.referrals.entry(referrer_id).or_insert_with(Vec::new).push(referral_record); |
| 473 |
} |
| 474 |
|
| 475 |
Ok(()) |
| 476 |
} |
| 477 |
|
| 478 |
/// Get contribution tracker reference |
| 479 |
pub fn get_contribution_tracker(&self) -> &ContributionTracker { |
| 480 |
&self.contribution_tracker |
| 481 |
} |
| 482 |
|
| 483 |
/// Get resource manager reference |
| 484 |
pub fn get_resource_manager(&self) -> &ResourceManager { |
| 485 |
&self.resource_manager |
| 486 |
} |
| 487 |
|
| 488 |
/// Get allocation queue reference |
| 489 |
pub fn get_allocation_queue(&self) -> &AllocationQueue { |
| 490 |
&self.allocation_queue |
| 491 |
} |
| 492 |
} |
| 493 |
|
| 494 |
impl Default for ContributionEconomicManager { |
| 495 |
fn default() -> Self { |
| 496 |
Self::new() |
| 497 |
} |
| 498 |
} |