rustywallet_vanity/
difficulty.rs1use crate::address_type::AddressType;
4use crate::pattern::Pattern;
5use std::time::Duration;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum DifficultyLevel {
10 Easy,
12 Medium,
14 Hard,
16 VeryHard,
18 Extreme,
20}
21
22impl DifficultyLevel {
23 pub fn description(&self) -> &'static str {
25 match self {
26 DifficultyLevel::Easy => "Easy (< 1 minute)",
27 DifficultyLevel::Medium => "Medium (1-10 minutes)",
28 DifficultyLevel::Hard => "Hard (10-60 minutes)",
29 DifficultyLevel::VeryHard => "Very Hard (1-24 hours)",
30 DifficultyLevel::Extreme => "Extreme (> 24 hours)",
31 }
32 }
33}
34
35impl std::fmt::Display for DifficultyLevel {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 write!(f, "{}", self.description())
38 }
39}
40
41#[derive(Debug, Clone)]
43pub struct DifficultyEstimate {
44 pub pattern: Pattern,
46 pub probability: f64,
48 pub expected_attempts: u64,
50 pub estimated_time: Duration,
52 pub difficulty_level: DifficultyLevel,
54}
55
56impl DifficultyEstimate {
57 pub fn calculate(
59 pattern: &Pattern,
60 address_type: AddressType,
61 case_sensitive: bool,
62 generation_rate: f64,
63 ) -> Self {
64 let expected_attempts = pattern.difficulty(address_type, case_sensitive) as u64;
65 let probability = 1.0 / expected_attempts as f64;
66
67 let estimated_secs = if generation_rate > 0.0 {
68 expected_attempts as f64 / generation_rate
69 } else {
70 f64::INFINITY
71 };
72 let estimated_time = Duration::from_secs_f64(estimated_secs.min(f64::MAX / 2.0));
73
74 let difficulty_level = Self::categorize_difficulty(estimated_time);
75
76 Self {
77 pattern: pattern.clone(),
78 probability,
79 expected_attempts,
80 estimated_time,
81 difficulty_level,
82 }
83 }
84
85 fn categorize_difficulty(estimated_time: Duration) -> DifficultyLevel {
87 let secs = estimated_time.as_secs();
88 if secs < 60 {
89 DifficultyLevel::Easy
90 } else if secs < 600 {
91 DifficultyLevel::Medium
92 } else if secs < 3600 {
93 DifficultyLevel::Hard
94 } else if secs < 86400 {
95 DifficultyLevel::VeryHard
96 } else {
97 DifficultyLevel::Extreme
98 }
99 }
100
101 pub fn is_practical(&self) -> bool {
103 matches!(
104 self.difficulty_level,
105 DifficultyLevel::Easy | DifficultyLevel::Medium | DifficultyLevel::Hard
106 )
107 }
108
109 pub fn warning(&self) -> Option<String> {
111 match self.difficulty_level {
112 DifficultyLevel::VeryHard => Some(format!(
113 "Warning: Pattern '{}' is very difficult. Expected time: {:?}",
114 self.pattern, self.estimated_time
115 )),
116 DifficultyLevel::Extreme => Some(format!(
117 "Warning: Pattern '{}' is extremely difficult and may take days or longer. Expected time: {:?}",
118 self.pattern, self.estimated_time
119 )),
120 _ => None,
121 }
122 }
123}
124
125impl std::fmt::Display for DifficultyEstimate {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 write!(
128 f,
129 "Pattern: {}, Difficulty: {}, Expected attempts: {}, Estimated time: {:?}",
130 self.pattern, self.difficulty_level, self.expected_attempts, self.estimated_time
131 )
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_difficulty_levels() {
141 assert_eq!(
142 DifficultyEstimate::categorize_difficulty(Duration::from_secs(30)),
143 DifficultyLevel::Easy
144 );
145 assert_eq!(
146 DifficultyEstimate::categorize_difficulty(Duration::from_secs(300)),
147 DifficultyLevel::Medium
148 );
149 assert_eq!(
150 DifficultyEstimate::categorize_difficulty(Duration::from_secs(1800)),
151 DifficultyLevel::Hard
152 );
153 }
154
155 #[test]
156 fn test_difficulty_estimate() {
157 let pattern = Pattern::prefix("1A").unwrap();
158 let estimate =
159 DifficultyEstimate::calculate(&pattern, AddressType::P2PKH, true, 1_000_000.0);
160
161 assert_eq!(estimate.difficulty_level, DifficultyLevel::Easy);
163 assert!(estimate.is_practical());
164 }
165
166 #[test]
167 fn test_hard_pattern() {
168 let pattern = Pattern::prefix("1Bitcoin").unwrap();
169 let estimate =
170 DifficultyEstimate::calculate(&pattern, AddressType::P2PKH, true, 1_000_000.0);
171
172 assert!(matches!(
174 estimate.difficulty_level,
175 DifficultyLevel::VeryHard | DifficultyLevel::Extreme
176 ));
177 }
178}