modality_utils/
hash_tax.rs1use sha1::{Sha1, Digest};
2use sha2::{Sha256, Sha384, Sha512};
3use std::collections::HashMap;
4use std::error::Error;
5use num_bigint::BigUint;
6use num_bigint::ToBigUint;
7use num_traits::Num;
8
9const DEFAULT_MAX_TRIES: u128 = 100_000_000_000;
10const DEFAULT_HASH_FUNC_NAME: &str = "sha256";
11const DEFAULT_DIFFICULTY_COEFFICIENT: u128 = 0xffff;
12const DEFAULT_DIFFICULTY_EXPONENT: u128 = 0x1d;
13const DEFAULT_DIFFICULTY_BASE: u128 = 8;
14
15lazy_static::lazy_static! {
16 static ref HASH_FUNC_HEXADECIMAL_LENGTH: HashMap<&'static str, usize> = {
17 let mut map = HashMap::new();
18 map.insert("sha1", 40);
19 map.insert("sha256", 64);
20 map.insert("sha384", 96);
21 map.insert("sha512", 128);
22 map
23 };
24}
25
26#[allow(dead_code)]
27pub fn mine(
28 data: &str,
29 difficulty: u128,
30 max_tries: Option<u128>,
31 hash_func_name: Option<&str>,
32) -> Result<u128, Box<dyn Error>> {
33 let max_tries = max_tries.unwrap_or(DEFAULT_MAX_TRIES);
34 let hash_func_name = hash_func_name.unwrap_or(DEFAULT_HASH_FUNC_NAME);
35
36 let mut nonce = 0;
37 let mut try_count = 0;
38
39 while try_count < max_tries {
40 try_count += 1;
41 let hash = hash_with_nonce(data, nonce, hash_func_name)?;
42 if is_hash_acceptable(&hash, difficulty, hash_func_name) {
43 return Ok(nonce);
44 }
45 nonce += 1;
46 }
47
48 Err("maxTries reached, no nonce found".into())
49}
50
51#[allow(dead_code)]
52pub fn hash_with_nonce(data: &str, nonce: u128, hash_func_name: &str) -> Result<String, Box<dyn Error>> {
53 let hash = match hash_func_name {
54 "sha1" => {
55 let mut hasher = Sha1::new();
56 hasher.update(format!("{}{}", data, nonce));
57 format!("{:x}", hasher.finalize())
58 }
59 "sha256" => {
60 let mut hasher = Sha256::new();
61 hasher.update(format!("{}{}", data, nonce));
62 format!("{:x}", hasher.finalize())
63 }
64 "sha384" => {
65 let mut hasher = Sha384::new();
66 hasher.update(format!("{}{}", data, nonce));
67 format!("{:x}", hasher.finalize())
68 }
69 "sha512" => {
70 let mut hasher = Sha512::new();
71 hasher.update(format!("{}{}", data, nonce));
72 format!("{:x}", hasher.finalize())
73 }
74 _ => return Err(format!("Unsupported hash function: {}", hash_func_name).into()),
75 };
76
77 Ok(hash)
78}
79
80pub fn difficulty_to_target_hash(
81 difficulty: u128,
82 hash_func_name: &str,
83 coefficient: u128,
84 exponent: u128,
85 base: u128,
86) -> String {
87 let _hex_length = HASH_FUNC_HEXADECIMAL_LENGTH[hash_func_name];
88 let max_target = coefficient.to_biguint().unwrap() << (exponent * base);
89 let target_bignum = max_target / difficulty;
90 target_bignum.to_str_radix(16)
91}
92
93pub fn is_hash_acceptable(hash: &str, difficulty: u128, hash_func_name: &str) -> bool {
94 let target_hash = difficulty_to_target_hash(difficulty, hash_func_name, DEFAULT_DIFFICULTY_COEFFICIENT, DEFAULT_DIFFICULTY_EXPONENT, DEFAULT_DIFFICULTY_BASE);
95 let hash_big_int = BigUint::from_str_radix(hash, 16).unwrap();
96 let target_big_int = BigUint::from_str_radix(&target_hash, 16).unwrap();
97 hash_big_int < target_big_int
98}
99
100#[allow(dead_code)]
101pub fn validate_nonce(data: &str, nonce: u128, difficulty: u128, hash_func_name: &str) -> Result<bool, Box<dyn Error>> {
102 let hash = hash_with_nonce(data, nonce, hash_func_name)?;
103 Ok(is_hash_acceptable(&hash, difficulty, hash_func_name))
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn it_works() {
112 let data = String::from("data");
113 let nonce = mine(&data, 500, None, None).unwrap();
114 assert_eq!(nonce, 2401);
115 }
116}