|
1 | | -use sha1::{Digest, Sha1}; |
2 | | - |
3 | 1 | use std::collections::HashMap; |
4 | 2 | use std::sync::Arc; |
5 | 3 |
|
6 | | -use tokio::sync::{Mutex, RwLock}; |
| 4 | +use tokio::sync::Mutex; |
| 5 | + |
| 6 | +use hex::FromHex; |
| 7 | +use sha1::{Digest, Sha1}; |
7 | 8 |
|
8 | | -fn sha1_digest(input: &str) -> String { |
9 | | - let mut hasher = Sha1::new(); |
10 | | - sha1::Digest::update(&mut hasher, input.as_bytes()); |
| 9 | +type BlockHash = [u8; 20]; |
11 | 10 |
|
12 | | - let h = hasher.finalize(); |
13 | | - format!("{:x}", h) |
| 11 | +fn to_block_hash(s: &str) -> BlockHash { |
| 12 | + <BlockHash>::from_hex(s).unwrap_or_else(|_| panic!("{} is not hex string.", s)) |
14 | 13 | } |
15 | 14 |
|
16 | 15 | #[derive(Clone)] |
17 | 16 | pub struct Sha1Hasher { |
18 | | - hashmap: Arc<RwLock<HashMap<String, Vec<String>>>>, |
19 | | - stack: Arc<Mutex<Vec<String>>>, |
| 17 | + hashmap: Arc<Mutex<HashMap<BlockHash, Vec<BlockHash>>>>, |
| 18 | + stack: Arc<Mutex<Vec<BlockHash>>>, |
20 | 19 | } |
21 | 20 |
|
22 | 21 | impl Sha1Hasher { |
23 | | - const HASHMAP_LIMIT: usize = 100; |
| 22 | + const HASHMAP_LIMIT: usize = 10; |
24 | 23 |
|
25 | 24 | pub fn new() -> Self { |
26 | 25 | Self { |
27 | | - hashmap: Arc::new(RwLock::new(HashMap::new())), |
28 | | - stack: Arc::new(Mutex::new(Vec::new())), |
| 26 | + hashmap: Arc::new(Mutex::new(HashMap::with_capacity(Self::HASHMAP_LIMIT * 2))), |
| 27 | + stack: Arc::new(Mutex::new(Vec::with_capacity(Self::HASHMAP_LIMIT * 2))), |
29 | 28 | } |
30 | 29 | } |
31 | 30 |
|
32 | | - pub async fn get_hash(&self, last_block_hash: String, expected_hash: String, diff: u32) -> u32 { |
33 | | - { |
34 | | - let hashmap = self.hashmap.read().await; |
35 | | - if let Some(hashes) = hashmap.get(&last_block_hash) { |
36 | | - if hashes.len() < diff as usize { |
37 | | - log::warn!("Diff changed, recalculating."); |
38 | | - } else { |
39 | | - // Optimized for lower difficulty, uses AVX. |
40 | | - for (duco_numeric_result, hash) in hashes.iter().enumerate() { |
41 | | - if hash == &expected_hash { |
42 | | - return duco_numeric_result as u32; |
43 | | - } |
44 | | - } |
| 31 | + pub async fn get_hash(&self, last_block_hash: &str, expected_hash: &str, diff: u32) -> u32 { |
| 32 | + let last_block_hash = to_block_hash(last_block_hash); |
| 33 | + let expected_hash = to_block_hash(expected_hash); |
| 34 | + |
| 35 | + let mut hashmap = self.hashmap.lock().await; |
| 36 | + if let Some(hashes) = hashmap.get_mut(&last_block_hash) { |
| 37 | + // Optimized for lower difficulty, uses AVX. |
| 38 | + for (duco_numeric_result, hash) in hashes.iter().enumerate() { |
| 39 | + if hash == &expected_hash { |
| 40 | + return duco_numeric_result as u32; |
45 | 41 | } |
46 | 42 | } |
47 | | - } // Unlock hashmap |
48 | 43 |
|
49 | | - let hashes = self.precompute(last_block_hash, diff).await; |
| 44 | + let current_progress = hashes.len() as u32; |
| 45 | + if current_progress < diff { |
| 46 | + log::info!("Continuing calculation."); |
50 | 47 |
|
51 | | - for (duco_numeric_result, hash) in hashes.iter().enumerate() { |
52 | | - if hash == &expected_hash { |
53 | | - return duco_numeric_result as u32; |
| 48 | + for duco_numeric_result in current_progress..diff { |
| 49 | + let hash = self.hash_next_block(&last_block_hash, duco_numeric_result); |
| 50 | + hashes.push(hash); |
| 51 | + |
| 52 | + if hash == expected_hash { |
| 53 | + return duco_numeric_result as u32; |
| 54 | + } |
| 55 | + } |
54 | 56 | } |
55 | 57 | } |
56 | 58 |
|
57 | | - return 0; |
58 | | - } |
59 | | - |
60 | | - async fn precompute(&self, last_block_hash: String, diff: u32) -> Vec<String> { |
61 | | - let mut hashmap = self.hashmap.write().await; |
62 | 59 | let mut stack = self.stack.lock().await; |
63 | 60 |
|
64 | | - if hashmap.len() > Self::HASHMAP_LIMIT { |
| 61 | + if hashmap.len() > Self::HASHMAP_LIMIT * 2 { |
| 62 | + log::warn!("Too many hashes stored. Freeing."); |
| 63 | + |
65 | 64 | for _ in 0..(hashmap.len() - Self::HASHMAP_LIMIT) { |
66 | 65 | let k = stack.remove(0); |
67 | 66 | hashmap.remove(&k); |
68 | 67 | } |
69 | 68 | } |
70 | 69 |
|
71 | | - let hashes: Vec<String> = (0..diff) |
72 | | - .map(|duco_numeric_result| format!("{}{}", last_block_hash, duco_numeric_result)) |
73 | | - .map(|h| sha1_digest(h.as_str())) |
74 | | - .collect(); |
| 70 | + let mut hashes: Vec<BlockHash> = Vec::with_capacity(diff as usize); |
| 71 | + for duco_numeric_result in 0..diff { |
| 72 | + let hash = self.hash_next_block(&last_block_hash, duco_numeric_result); |
| 73 | + hashes.push(hash); |
| 74 | + |
| 75 | + if hash == expected_hash { |
| 76 | + hashmap.insert(last_block_hash, hashes); |
| 77 | + stack.push(last_block_hash); |
| 78 | + |
| 79 | + return duco_numeric_result; |
| 80 | + } |
| 81 | + } |
75 | 82 |
|
76 | | - hashmap.insert(last_block_hash.clone(), hashes.clone()); |
| 83 | + hashmap.insert(last_block_hash, hashes); |
77 | 84 | stack.push(last_block_hash); |
78 | 85 |
|
79 | | - hashes |
| 86 | + 0 |
| 87 | + } |
| 88 | + |
| 89 | + fn hash_next_block(&self, last_block_hash: &BlockHash, duco_numeric_result: u32) -> BlockHash { |
| 90 | + let mut hasher = Sha1::new(); |
| 91 | + |
| 92 | + let mut encode_slice: [u8; 40] = [0; 40]; |
| 93 | + hex::encode_to_slice(&last_block_hash, &mut encode_slice).unwrap(); |
| 94 | + |
| 95 | + sha1::Digest::update(&mut hasher, &encode_slice); |
| 96 | + sha1::Digest::update(&mut hasher, duco_numeric_result.to_string().as_bytes()); |
| 97 | + let h = hasher.finalize(); |
| 98 | + |
| 99 | + let mut hash: [u8; 20] = [0; 20]; |
| 100 | + hash.copy_from_slice(&h); |
| 101 | + |
| 102 | + hash |
80 | 103 | } |
81 | 104 | } |
0 commit comments