rustywallet_vanity/
config.rs

1//! Configuration for vanity address generation.
2
3use crate::address_type::AddressType;
4use crate::error::VanityError;
5use crate::pattern::Pattern;
6use std::time::Duration;
7
8/// Configuration for vanity address generation.
9#[derive(Debug, Clone)]
10pub struct VanityConfig {
11    /// Patterns to search for.
12    pub patterns: Vec<Pattern>,
13    /// Address type to generate.
14    pub address_type: AddressType,
15    /// Whether to use testnet.
16    pub testnet: bool,
17    /// Whether matching is case-sensitive.
18    pub case_sensitive: bool,
19    /// Maximum number of attempts before giving up.
20    pub max_attempts: Option<u64>,
21    /// Maximum time to search.
22    pub timeout: Option<Duration>,
23    /// Number of threads for parallel search.
24    pub thread_count: Option<usize>,
25    /// Batch size for key generation.
26    pub batch_size: usize,
27    /// Interval for progress callbacks.
28    pub progress_interval: Duration,
29}
30
31impl Default for VanityConfig {
32    fn default() -> Self {
33        Self {
34            patterns: Vec::new(),
35            address_type: AddressType::P2PKH,
36            testnet: false,
37            case_sensitive: true,
38            max_attempts: None,
39            timeout: None,
40            thread_count: None,
41            batch_size: 10_000,
42            progress_interval: Duration::from_secs(1),
43        }
44    }
45}
46
47impl VanityConfig {
48    /// Create a new default configuration.
49    pub fn new() -> Self {
50        Self::default()
51    }
52
53    /// Create a fast configuration optimized for speed.
54    pub fn fast() -> Self {
55        Self {
56            batch_size: 50_000,
57            progress_interval: Duration::from_millis(500),
58            ..Default::default()
59        }
60    }
61
62    /// Create a thorough configuration with more patterns.
63    pub fn thorough() -> Self {
64        Self {
65            batch_size: 10_000,
66            progress_interval: Duration::from_secs(2),
67            ..Default::default()
68        }
69    }
70
71    /// Set the patterns to search for.
72    pub fn with_patterns(mut self, patterns: Vec<Pattern>) -> Self {
73        self.patterns = patterns;
74        self
75    }
76
77    /// Add a pattern to search for.
78    pub fn with_pattern(mut self, pattern: Pattern) -> Self {
79        self.patterns.push(pattern);
80        self
81    }
82
83    /// Set the address type.
84    pub fn with_address_type(mut self, address_type: AddressType) -> Self {
85        self.address_type = address_type;
86        self
87    }
88
89    /// Set whether to use testnet.
90    pub fn with_testnet(mut self, testnet: bool) -> Self {
91        self.testnet = testnet;
92        self
93    }
94
95    /// Set case sensitivity.
96    pub fn with_case_sensitive(mut self, case_sensitive: bool) -> Self {
97        self.case_sensitive = case_sensitive;
98        self
99    }
100
101    /// Set maximum attempts.
102    pub fn with_max_attempts(mut self, max: u64) -> Self {
103        self.max_attempts = Some(max);
104        self
105    }
106
107    /// Set timeout.
108    pub fn with_timeout(mut self, timeout: Duration) -> Self {
109        self.timeout = Some(timeout);
110        self
111    }
112
113    /// Set thread count.
114    pub fn with_thread_count(mut self, count: usize) -> Self {
115        self.thread_count = Some(count);
116        self
117    }
118
119    /// Set batch size.
120    pub fn with_batch_size(mut self, size: usize) -> Self {
121        self.batch_size = size;
122        self
123    }
124
125    /// Validate the configuration.
126    pub fn validate(&self) -> Result<(), VanityError> {
127        if self.patterns.is_empty() {
128            return Err(VanityError::InvalidConfig(
129                "At least one pattern is required".to_string(),
130            ));
131        }
132
133        if self.batch_size == 0 {
134            return Err(VanityError::InvalidConfig(
135                "Batch size must be greater than 0".to_string(),
136            ));
137        }
138
139        if let Some(count) = self.thread_count {
140            if count == 0 {
141                return Err(VanityError::InvalidConfig(
142                    "Thread count must be greater than 0".to_string(),
143                ));
144            }
145        }
146
147        // Validate each pattern for the address type
148        for pattern in &self.patterns {
149            pattern
150                .validate_for_type(self.address_type, self.testnet)
151                .map_err(VanityError::InvalidPattern)?;
152        }
153
154        Ok(())
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    #[test]
163    fn test_default_config() {
164        let config = VanityConfig::default();
165        assert!(config.patterns.is_empty());
166        assert_eq!(config.address_type, AddressType::P2PKH);
167        assert!(config.case_sensitive);
168    }
169
170    #[test]
171    fn test_fast_preset() {
172        let config = VanityConfig::fast();
173        assert_eq!(config.batch_size, 50_000);
174    }
175
176    #[test]
177    fn test_validation_no_patterns() {
178        let config = VanityConfig::default();
179        assert!(config.validate().is_err());
180    }
181
182    #[test]
183    fn test_validation_with_pattern() {
184        let config = VanityConfig::default()
185            .with_pattern(Pattern::prefix("1Love").unwrap());
186        assert!(config.validate().is_ok());
187    }
188
189    #[test]
190    fn test_builder_pattern() {
191        let config = VanityConfig::new()
192            .with_address_type(AddressType::P2WPKH)
193            .with_case_sensitive(false)
194            .with_max_attempts(1_000_000)
195            .with_timeout(Duration::from_secs(60));
196
197        assert_eq!(config.address_type, AddressType::P2WPKH);
198        assert!(!config.case_sensitive);
199        assert_eq!(config.max_attempts, Some(1_000_000));
200    }
201}