1use blake2::{Blake2s256, Digest};
25use rand_core::{OsRng, RngCore};
26
27struct Entropy {
28 entropy: [u8; 32],
29}
30
31impl Entropy {
32 fn new() -> Self {
33 let mut entropy = [0u8; 32];
34 OsRng::default().fill_bytes(&mut entropy);
35
36 Entropy { entropy }
37 }
38
39 fn from(entropy: [u8; 32]) -> Self {
40 Entropy { entropy }
41 }
42
43 fn hash(&self) -> [u8; 32] {
44 let mut hash = Blake2s256::new();
45 let _ = hash.update(self.entropy);
46 hash.finalize().into()
47 }
48}
49
50struct HashPrefix {
51 zero_bits: u8,
52}
53
54impl Default for HashPrefix {
55 fn default() -> Self {
56 HashPrefix { zero_bits: 20u8 }
57 }
58}
59
60impl HashPrefix {
61 fn new(leading_zeros: u8) -> Self {
62 HashPrefix {
63 zero_bits: 8 / 2 * leading_zeros,
64 }
65 }
66}
67
68impl HashPrefix {
69 fn get(&self) -> u8 {
70 self.zero_bits
71 }
72
73 fn target(&self) -> [u8; 32] {
74 let target: [u8; 16] = self.target_u128().to_be_bytes();
75 let mut array: [u8; 32] = [255u8; 32];
76 array[0..16].copy_from_slice(target.as_slice());
77 array
78 }
79
80 fn target_u128(&self) -> u128 {
81 let total_bits = 128;
82 let remaining_bits = total_bits - self.get();
83
84 match self.get() < total_bits {
85 true => (1u128 << remaining_bits) - 1,
86 false => 1,
87 }
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
94pub struct HashFinder {
95 target: [u8; 32],
96}
97
98impl Default for HashFinder {
99 fn default() -> Self {
100 HashFinder {
101 target: HashPrefix::default().target(),
102 }
103 }
104}
105
106impl HashFinder {
107 pub fn new(leading_zeros: u8) -> Self {
116 HashFinder {
117 target: HashPrefix::new(leading_zeros).target(),
118 }
119 }
120
121 pub fn find(&self) -> [u8; 32] {
150 loop {
151 let origin_hash = Entropy::new().hash();
152 let target_hash = Entropy::from(origin_hash).hash();
153 match target_hash < self.target {
154 true => return origin_hash,
155 false => continue,
156 }
157 }
158 }
159
160 pub fn check(&self, origin_hash: String) -> Result<bool, hex::FromHexError> {
198 let mut origin_hash_bytes: [u8; 32] = [0u8; 32];
199 let _ = hex::decode_to_slice(origin_hash, &mut origin_hash_bytes)?;
200
201 let target_hash_bytes = Entropy::from(origin_hash_bytes).hash();
202
203 Ok(target_hash_bytes < self.target)
204 }
205}
206
207#[cfg(test)]
208mod pow_account {
209
210 use super::*;
211 use hex::FromHexError;
212
213 #[test]
214 fn new_entropy_has_a_length_of_32() {
215 let entropy = Entropy::new().entropy;
216 assert!(entropy.len().eq(&32))
217 }
218
219 #[test]
220 fn new_entropy_is_unique() {
221 let entropy_a = Entropy::new().entropy;
222 let entropy_b = Entropy::new().entropy;
223 assert_ne!(entropy_a, entropy_b)
224 }
225
226 #[test]
227 fn entropy_generates_256bit_hash() {
228 let hash = Entropy::new().hash();
229 assert!(hash.len().eq(&32))
230 }
231
232 #[test]
233 fn entropy_created_from_a_set_of_bytes() {
234 let entropy_a = Entropy::new().entropy;
235 let entropy_b = Entropy::from(entropy_a);
236 assert_eq!(entropy_b.entropy, entropy_a)
237 }
238
239 #[test]
240 fn blake2s_hash_can_be_validated() {
241 let origin_hash = "c37289b48949a7d172346cb3e5600da905f53e7c022d364836dcf57db4de33fa";
242 let target_hash = "7478987293e1864fd833ae3607bc99b9b22e7ca39bced21c3b0428bd9c7218ba";
243
244 let origin_hash_vec = hex::decode(origin_hash).unwrap();
245 let origin_hash_bytes: [u8; 32] = origin_hash_vec.try_into().unwrap();
246
247 let entropy = Entropy::from(origin_hash_bytes);
248 let origin_hash_hex = hex::encode(entropy.hash()).to_string();
249 assert_eq!(origin_hash_hex, target_hash)
250 }
251
252 #[test]
253 fn new_hash_prefix_has_non_zero_number_of_bits() {
254 let hash_prefix = HashPrefix::default();
255 assert!(hash_prefix.get() > 0)
256 }
257
258 #[test]
259 fn default_target_matches_max_value() {
260 let target = HashPrefix::default().target();
261 let max_target: [u8; 32] = [
262 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
263 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
264 0xff, 0xff, 0xff, 0xff,
265 ];
266
267 assert_eq!(target, max_target)
268 }
269
270 #[test]
271 fn can_find_a_hash_which_starts_from_a_specific_pattern() {
272 let origin_hash = HashFinder::new(4).find();
273 let target_hash = Entropy::from(origin_hash).hash();
274
275 let hash_hex = hex::encode(target_hash);
276 assert!(hash_hex.starts_with("0000"))
277 }
278
279 #[test]
280 fn checks_the_hash_for_the_required_number_of_leading_zeros() {
281 let hash = String::from("3ca727c7fefed674268797882ff4b26c8e28873ee6fbfae71d9ccc35e24444d4");
282 assert!(HashFinder::new(3).check(hash).unwrap());
283
284 let hash = String::from("73b8f38be026335eb78946ea30434ff3cee4cff6544d49b4772f80397d40e72f");
285 assert!(HashFinder::new(4).check(hash).unwrap());
286
287 let hash = String::from("51ad0600f06b0d57300a37952cea658410488748400628c8a2e7d712892d806e");
288 assert!(HashFinder::new(5).check(hash).unwrap());
289
290 let hash = String::from("51ad0600f06b0d57300a37952cea658410488748400628c8a2e7d712892d806e");
291 assert!(HashFinder::default().check(hash).unwrap());
292
293 let hash = String::from("3ca727c7fefed674268797882ff4b26c8e28873ee6fbfae71d9ccc35e24444d4");
294 assert_eq!(HashFinder::new(4).check(hash).unwrap(), false);
295
296 let hash = String::from("3c+727c7fefed674268797882ff4b26c8e28873ee6fbfae71d9ccc35e24444d4");
297 let err = HashFinder::new(4).check(hash).unwrap_err();
298 assert_eq!(err, FromHexError::InvalidHexCharacter { c: '+', index: 2 });
299
300 let hash = String::from("3ca727c7fefed674268797882ff4b26c8e28873ee6fbfae71d9ccc35e24444d");
301 let err = HashFinder::new(4).check(hash).unwrap_err();
302 assert_eq!(err, FromHexError::OddLength);
303
304 let hash =
305 String::from("3ca727c7fefed674268797882ff4b26c8e28873ee6fbfae71d9ccc35e24444d444d4");
306 let err = HashFinder::new(4).check(hash).unwrap_err();
307 assert_eq!(err, FromHexError::InvalidStringLength)
308 }
309}