1use std::{
2 sync::{
3 atomic::{AtomicBool, AtomicUsize, Ordering},
4 Arc, Mutex,
5 },
6 time::{Duration, Instant},
7};
8
9use alloy::{
10 hex,
11 primitives::{address, Address, U256},
12 signers::{
13 k256::{ecdsa::SigningKey, FieldBytes},
14 local::{MnemonicBuilder, PrivateKeySigner},
15 utils::secret_key_to_address,
16 },
17};
18use coins_bip39::{English, Mnemonic};
19use rand::rngs::OsRng;
20use serde::{Deserialize, Serialize};
21
22pub trait AccountUtils {
23 fn store_mnemonic_wallet(phrase: &str, address: Address) -> crate::Result<()>;
24
25 fn store_private_key(private_key: &FieldBytes, address: Address) -> crate::Result<()>;
26
27 fn get_account_list() -> crate::Result<Vec<Address>>;
28
29 fn get_secret(address: &Address) -> crate::Result<Secret>;
30}
31
32pub struct AccountManager;
33
34impl AccountManager {
35 pub fn create_mnemonic_wallet() -> crate::Result<Address> {
36 let phrase = random_mnemonic()?;
37 Self::import_mnemonic_wallet(&phrase)
38 }
39
40 pub fn import_mnemonic_wallet(phrase: &str) -> crate::Result<Address> {
41 let address = get_address_from_mnemonic(phrase)?;
42 Self::store_mnemonic_wallet(phrase, address)?;
43 Ok(address)
44 }
45
46 pub fn import_private_key(private_key: &str) -> crate::Result<Address> {
47 let private_key = hex::decode(private_key)?;
48 let address = PrivateKeySigner::from_slice(&private_key)?.address();
49 Self::store_private_key(FieldBytes::from_slice(private_key.as_slice()), address)?;
50 Ok(address)
51 }
52
53 pub fn load_wallet(address: &Address) -> crate::Result<PrivateKeySigner> {
54 match Self::get_secret(address)? {
55 Secret::Mnemonic(phrase) => get_signer_from_mnemonic(&phrase),
56 Secret::PrivateKey(private_key) => {
57 Ok(PrivateKeySigner::from_slice(private_key.as_ref())?)
58 }
59 }
60 }
61}
62
63impl AccountUtils for AccountManager {
64 fn store_mnemonic_wallet(phrase: &str, address: Address) -> crate::Result<()> {
65 #[cfg(target_os = "macos")]
66 return macos::Macos::store_mnemonic_wallet(phrase, address);
67
68 #[cfg(target_os = "linux")]
69 return linux_insecure::LinuxInsecure::store_mnemonic_wallet(phrase, address);
70 }
71
72 fn store_private_key(private_key: &FieldBytes, address: Address) -> crate::Result<()> {
73 #[cfg(target_os = "macos")]
74 return macos::Macos::store_private_key(private_key, address);
75
76 #[cfg(target_os = "linux")]
77 return linux_insecure::LinuxInsecure::store_private_key(private_key, address);
78 }
79
80 fn get_account_list() -> crate::Result<Vec<Address>> {
81 #[cfg(target_os = "macos")]
82 return macos::Macos::get_account_list();
83
84 #[cfg(target_os = "linux")]
85 return linux_insecure::LinuxInsecure::get_account_list();
86 }
87
88 fn get_secret(address: &Address) -> crate::Result<Secret> {
89 #[cfg(target_os = "macos")]
90 return macos::Macos::get_secret(address);
91
92 #[cfg(target_os = "linux")]
93 return linux_insecure::LinuxInsecure::get_secret(address);
94 }
95}
96
97fn random_mnemonic() -> crate::Result<String> {
98 let mnemonic = Mnemonic::<English>::new_with_count(&mut OsRng, 24)?;
99 Ok(mnemonic.to_phrase())
100}
101
102fn get_signer_from_mnemonic(phrase: &str) -> crate::Result<PrivateKeySigner> {
103 let signer = MnemonicBuilder::<English>::default()
104 .phrase(phrase)
105 .build()?;
106 Ok(signer)
107}
108
109fn get_address_from_mnemonic(phrase: &str) -> crate::Result<Address> {
110 let signer = get_signer_from_mnemonic(phrase)?;
111 Ok(signer.address())
112}
113
114pub fn mine_wallet(
115 mask_a: Address,
116 mask_b: Address,
117 max_dur: Option<Duration>,
118 shutdown_signal: Arc<AtomicBool>,
119) -> crate::Result<(Option<SigningKey>, usize, Duration)> {
120 let address_one = address!("0xffffffffffffffffffffffffffffffffffffffff");
121 let counter = Arc::new(AtomicUsize::new(0));
122 let stop = Arc::new(AtomicBool::new(false));
123 let result = Arc::new(Mutex::new(None));
124 let start = Instant::now();
125
126 rayon::scope(|s| {
127 for _ in 0..rayon::current_num_threads() {
128 let counter = Arc::clone(&counter);
129 let stop = Arc::clone(&stop);
130 let result = Arc::clone(&result);
131 let shutdown_signal = shutdown_signal.clone();
132 s.spawn(move |_| {
133 let key = coins_bip32::prelude::SigningKey::random(&mut OsRng);
135 let mut u = U256::from_be_slice(&key.to_bytes());
136
137 while !stop.load(Ordering::Relaxed) && !shutdown_signal.load(Ordering::Relaxed) {
138 if let Some(max_dur) = max_dur {
139 if Instant::now().duration_since(start) > max_dur {
140 break;
141 }
142 }
143
144 if let Ok(credential) =
145 SigningKey::from_bytes(FieldBytes::from_slice(&u.to_be_bytes_vec()))
146 {
147 let address = secret_key_to_address(&credential);
148 if address.bit_and(mask_a) == mask_a
149 && address.bit_xor(address_one).bit_and(mask_b) == mask_b
150 {
151 stop.store(true, Ordering::Relaxed);
152 let mut result = result.lock().unwrap();
153 *result = Some(credential);
154 };
155 } else {
156 }
158 u += U256::ONE;
160 counter.fetch_add(1, Ordering::Relaxed);
161 }
162 });
163 }
164 });
165
166 let result = result.lock().unwrap().clone();
167 let counter = counter.load(Ordering::Relaxed);
168 Ok((result, counter, Instant::now().duration_since(start)))
169}
170
171#[derive(Clone, Debug)]
172pub enum Secret {
173 Mnemonic(String),
174 PrivateKey(FieldBytes),
175}
176
177impl Serialize for Secret {
178 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
179 where
180 S: serde::Serializer,
181 {
182 match self {
183 Secret::Mnemonic(mnemonic) => serializer.serialize_str(mnemonic),
184 Secret::PrivateKey(private_key) => {
185 let hex = hex::encode(private_key);
186 serializer.serialize_str(&hex)
187 }
188 }
189 }
190}
191
192impl<'de> Deserialize<'de> for Secret {
193 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
194 where
195 D: serde::Deserializer<'de>,
196 {
197 let s = String::deserialize(deserializer)?;
198 if s.len() == 64 {
199 let mut bytes = [0u8; 32];
200 hex::decode_to_slice(&s, &mut bytes).map_err(serde::de::Error::custom)?;
201 Ok(Secret::PrivateKey(*FieldBytes::from_slice(
202 bytes.as_slice(),
203 )))
204 } else {
205 Ok(Secret::Mnemonic(s))
206 }
207 }
208}
209
210#[cfg(target_os = "macos")]
211mod macos {
212 use core_foundation::{
213 base::{CFCopyDescription, CFGetTypeID, TCFType},
214 data::CFData,
215 date::CFDate,
216 dictionary::CFDictionary,
217 string::CFString,
218 };
219 use security_framework::{
220 item::{ItemClass, ItemSearchOptions, SearchResult},
221 os::macos::keychain::SecKeychain,
222 };
223 use std::collections::HashMap;
224
225 use super::*;
226
227 fn keychain() -> SecKeychain {
228 SecKeychain::default().expect("SecKeychain::default() - accessing default keychain failed")
229 }
230
231 pub struct Macos;
232
233 impl AccountUtils for Macos {
234 fn store_mnemonic_wallet(phrase: &str, address: Address) -> crate::Result<()> {
235 let mnemonic_service = format!("gm:mnemonic:{address}");
236
237 keychain()
238 .add_generic_password(&mnemonic_service, &address.to_string(), phrase.as_bytes())
239 .map_err(|e| crate::Error::AppleSecurityFrameworkError(Box::new(e)))?;
240
241 Ok(())
242 }
243
244 fn store_private_key(private_key: &FieldBytes, address: Address) -> crate::Result<()> {
245 let pk_service = format!("gm:{address}");
246
247 keychain()
248 .add_generic_password(
249 &pk_service,
250 &address.to_string(),
251 hex::encode(private_key).as_bytes(),
252 )
253 .map_err(|e| crate::Error::AppleSecurityFrameworkError(Box::new(e)))?;
254
255 Ok(())
256 }
257
258 fn get_account_list() -> crate::Result<Vec<Address>> {
259 let mut search = ItemSearchOptions::default();
260 search.class(ItemClass::generic_password());
261 search.limit(1000);
263 search.load_attributes(true);
264
265 let mut accounts = vec![];
266
267 if let Ok(result) = search.search() {
268 for item in result {
269 if let SearchResult::Dict(item) = item {
270 let item = simplify_dict(&item);
271 let service = item.get("svce");
272 if let Some(service) = service {
273 if service.starts_with("gm") {
274 let addr_str =
275 item.get("acct").expect("must have an account address");
276 accounts.push(addr_str.parse()?);
277 }
278 }
279 }
280 }
281 }
282
283 Ok(accounts)
284 }
285
286 fn get_secret(address: &Address) -> crate::Result<Secret> {
287 let mnemonic_signer = || {
288 let mnemonic_service = format!("gm:mnemonic:{address}");
289 keychain()
290 .find_generic_password(&mnemonic_service, &address.to_string())
291 .map_err(crate::Error::from)
292 .and_then(|(pswd, _item)| {
293 String::from_utf8(pswd.to_vec())
294 .map_err(crate::Error::from)
295 .map(Secret::Mnemonic)
296 })
297 };
298 let pk_signer = || {
299 let pk_service = format!("gm:{address}");
300 keychain()
301 .find_generic_password(&pk_service, &address.to_string())
302 .map_err(crate::Error::from)
303 .and_then(|(pswd, _item)| {
304 let raw_bytes = pswd.to_vec();
305 let hex_decoded = hex::decode(&raw_bytes);
306 let pk = hex_decoded.unwrap_or(raw_bytes);
307
308 SigningKey::from_slice(&pk)
309 .map_err(crate::Error::from)
310 .map(|key| Secret::PrivateKey(key.to_bytes()))
311 })
312 };
313
314 mnemonic_signer().or(pk_signer())
315 }
316 }
317
318 fn simplify_dict(dict: &CFDictionary) -> HashMap<String, String> {
319 unsafe {
320 let mut retmap = HashMap::new();
321 let (keys, values) = dict.get_keys_and_values();
322 for (k, v) in keys.iter().zip(values.iter()) {
323 let keycfstr = CFString::wrap_under_get_rule((*k).cast());
324 let val: String = match CFGetTypeID(*v) {
325 cfstring if cfstring == CFString::type_id() => {
326 format!("{}", CFString::wrap_under_get_rule((*v).cast()))
327 }
328 cfdata if cfdata == CFData::type_id() => {
329 let buf = CFData::wrap_under_get_rule((*v).cast());
330 let mut vec = Vec::new();
331 vec.extend_from_slice(buf.bytes());
332 format!("{}", String::from_utf8_lossy(&vec))
333 }
334 cfdate if cfdate == CFDate::type_id() => format!(
335 "{}",
336 CFString::wrap_under_create_rule(CFCopyDescription(*v))
337 ),
338 _ => String::from("unknown"),
339 };
340 retmap.insert(format!("{keycfstr}"), val);
341 }
342 retmap
343 }
344 }
345
346 #[cfg(test)]
347 mod test {
348 use crate::utils::account::AccountUtils;
349
350 #[test]
351 #[ignore]
352 fn see_all_accounts() {
353 let list = super::Macos::get_account_list();
354
355 println!("{list:#?}");
356 panic!();
357 }
358 }
359}
360
361pub mod linux_insecure {
362 use crate::disk::{DiskInterface, FileFormat};
363
364 use super::*;
365
366 pub struct LinuxInsecure;
367
368 impl AccountUtils for LinuxInsecure {
369 fn store_mnemonic_wallet(phrase: &str, address: Address) -> crate::Result<()> {
370 InsecurePrivateKeyStore::load()?.add(address, Secret::Mnemonic(phrase.to_string()))
371 }
372
373 fn store_private_key(private_key: &FieldBytes, address: Address) -> crate::Result<()> {
374 InsecurePrivateKeyStore::load()?.add(address, Secret::PrivateKey(*private_key))
375 }
376
377 fn get_account_list() -> crate::Result<Vec<Address>> {
378 Ok(InsecurePrivateKeyStore::load()?.list())
379 }
380
381 fn get_secret(address: &Address) -> crate::Result<Secret> {
382 InsecurePrivateKeyStore::load()?
383 .find_by_address(address)
384 .ok_or(crate::Error::SecretNotFound(*address))
385 }
386 }
387
388 #[derive(Serialize, Deserialize, Debug, Default)]
390 pub struct InsecurePrivateKeyStore {
391 pub keys: Vec<(Address, Secret)>,
392 }
393
394 impl DiskInterface for InsecurePrivateKeyStore {
395 const FILE_NAME: &'static str = "insecure_private_key_store";
396 const FORMAT: FileFormat = FileFormat::TOML;
397 }
398
399 impl InsecurePrivateKeyStore {
400 pub fn add(&mut self, address: Address, key: Secret) -> crate::Result<()> {
401 self.keys.push((address, key));
402 self.save()
403 }
404
405 pub fn find_by_address(&self, address: &Address) -> Option<Secret> {
406 self.keys.iter().find_map(|(stored_address, key)| {
407 if stored_address == address {
408 Some(key.clone())
409 } else {
410 None
411 }
412 })
413 }
414
415 pub fn list(self) -> Vec<Address> {
416 self.keys.into_iter().map(|(address, _)| address).collect()
417 }
418 }
419}