hashiverse_lib/tools/
pow.rs1use crate::tools::pow_required_estimator::PowRequiredEstimator;
6use crate::tools::time_provider::time_provider::{RealTimeProvider, TimeProvider};
7use crate::tools::types::{Hash, Pow, Salt};
8use crate::tools::{hashing, tools};
9use digest::consts::{U32, U64};
10
11use digest::generic_array::GenericArray;
12use digest::Digest;
13use log::trace;
14
15fn apply_hash<H>(data: &Hash) -> anyhow::Result<Hash>
16where H: Digest
17{
18 Hash::from_slice(&H::digest(data.as_ref()).as_slice()[0..32])
19}
20
21fn apply_chained_hash(algo_index: usize, hash_current: Hash) -> anyhow::Result<Hash> {
22
23 const ALGO_COUNT: usize = 17;
24 let algo_index = algo_index % ALGO_COUNT;
25
26 match algo_index {
27 0 => apply_hash::<blake2::Blake2s256>(&hash_current),
29 1 => apply_hash::<blake2::Blake2b512>(&hash_current),
30 2 => apply_hash::<sha2::Sha256>(&hash_current),
31 3 => apply_hash::<sha2::Sha384>(&hash_current),
32 4 => apply_hash::<sha2::Sha512>(&hash_current),
33 5 => apply_hash::<sha3::Sha3_256>(&hash_current),
34 6 => apply_hash::<sha3::Sha3_384>(&hash_current),
35 7 => apply_hash::<sha3::Sha3_512>(&hash_current),
36 8 => apply_hash::<sha3::Keccak256>(&hash_current),
37 9 => apply_hash::<sha3::Keccak384>(&hash_current),
38 10 => apply_hash::<sha3::Keccak512>(&hash_current),
39 11 => apply_hash::<groestl::Groestl256>(&hash_current),
40 12 => apply_hash::<groestl::Groestl512>(&hash_current),
41 13 => apply_hash::<whirlpool::Whirlpool>(&hash_current),
42
43 14 => {
45 let mut hasher = skein::Skein256::new();
46 hasher.update(hash_current.as_ref());
47 let hash_output: GenericArray<u8, U32> = hasher.finalize();
48 Hash::from_slice(&hash_output.as_slice()[0..32])
49 },
50 15 => {
51 let mut hasher = skein::Skein512::new();
52 hasher.update(hash_current.as_ref());
53 let hash_output: GenericArray<u8, U64> = hasher.finalize();
54 Hash::from_slice(&hash_output.as_slice()[0..32])
55 },
56 16 => {
57 let mut hasher = blake3::Hasher::new();
58 hasher.update(hash_current.as_ref());
59 let hash_output = hasher.finalize();
60 Hash::from_slice(&hash_output.as_bytes()[0..32])
61 },
62
63 _ => Ok(hash_current),
64 }
65}
66
67pub fn pow_compute_data_hash(datas: &[&[u8]]) -> Hash {
73 hashing::hash_multiple(datas)
74}
75
76pub fn pow_measure_from_data_hash(data_hash: &Hash, salt: &Salt) -> anyhow::Result<(Pow, Hash)> {
82 let mut data_current = hashing::hash_two(data_hash.as_ref(), salt.as_ref());
83
84 const CHAIN_LENGTH: usize = 5;
85 const MAX_REPETITIONS: usize = 2;
86
87 for _ in 0..CHAIN_LENGTH {
88 let algo_index = data_current.as_bytes()[0] as usize;
89 let repetitions = data_current.as_bytes()[1] as usize % MAX_REPETITIONS;
90
91 for _ in 0..=repetitions {
92 data_current = apply_chained_hash(algo_index, data_current)?;
93 }
94 }
95
96 let leading_zero_bits = tools::count_leading_zero_bits(data_current.as_bytes());
97 Ok((Pow(leading_zero_bits), data_current))
98}
99
100pub fn pow_measure(datas: &[&[u8]], salt: &Salt) -> anyhow::Result<(Pow, Hash)> {
101 pow_measure_from_data_hash(&pow_compute_data_hash(datas), salt)
102}
103
104pub async fn pow_generate_with_iteration_limit(iteration_limit: usize, pow_min: Pow, data_hash: &Hash) -> anyhow::Result<(Salt, Pow, Hash)> {
108 let mut salt: Salt;
109 let mut pow_best_so_far = (Salt::zero(), Pow(0), Hash::zero());
110
111 for _ in 0..iteration_limit {
112 salt = Salt::random();
113 let (pow, hash) = pow_measure_from_data_hash(data_hash, &salt)?;
114
115 if pow >= pow_min {
116 return Ok((salt, pow, hash));
117 }
118
119 if pow > pow_best_so_far.1 {
120 pow_best_so_far = (salt, pow, hash);
121 }
122 }
123
124 Ok(pow_best_so_far)
125}
126
127pub async fn pow_generate(pow_required: Pow, datas: &[&[u8]]) -> anyhow::Result<(Salt, Pow, Hash)> {
131 const BATCH_SIZE: usize = 64 * 1024;
132 let real_time_provider = RealTimeProvider::default();
133 let mut estimator = PowRequiredEstimator::new(real_time_provider.current_time_millis(), "pow_generate", pow_required);
134 let data_hash = pow_compute_data_hash(datas);
135 loop {
136 let result = pow_generate_with_iteration_limit(BATCH_SIZE, pow_required, &data_hash).await?;
137 if result.1 >= pow_required {
138 return Ok(result);
139 }
140
141 let progress = estimator.record_batch_and_estimate(real_time_provider.current_time_millis(), BATCH_SIZE, result.1);
142 trace!("{}", progress);
143 tools::yield_now().await;
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use crate::tools::pow::{pow_compute_data_hash, pow_generate, pow_measure, pow_measure_from_data_hash};
150 use crate::tools::tools;
151 use crate::tools::types::{Pow, Salt};
152
153 #[tokio::test]
154 async fn pow_test() {
155 for _ in 1..1000 {
156 let mut data1 = [0u8; 1024];
157 tools::random_fill_bytes(&mut data1);
158 let mut data2 = [0u8; 512];
159 tools::random_fill_bytes(&mut data2);
160
161 let salt = Salt::random();
162 let _pow = pow_measure(&[&data1, &data2], &salt);
163 }
164 }
165
166 #[tokio::test]
167 async fn pow_generate_test() -> anyhow::Result<()> {
168 const POW_MIN: Pow = Pow(16);
169
170 let mut data = [0u8; 1024];
171 tools::random_fill_bytes(&mut data);
172 let (salt, _, _) = pow_generate(POW_MIN, &[&data]).await?;
173 let (pow, _) = pow_measure(&[&data], &salt)?;
174 assert!(pow >= POW_MIN);
175
176 Ok(())
177 }
178
179 #[tokio::test]
182 async fn pow_measure_and_from_data_hash_agree() -> anyhow::Result<()> {
183 for _ in 0..200 {
184 let mut data1 = [0u8; 256];
185 tools::random_fill_bytes(&mut data1);
186 let mut data2 = [0u8; 128];
187 tools::random_fill_bytes(&mut data2);
188 let salt = Salt::random();
189
190 let (pow_direct, hash_direct) = pow_measure(&[&data1, &data2], &salt)?;
191 let data_hash = pow_compute_data_hash(&[&data1, &data2]);
192 let (pow_split, hash_split) = pow_measure_from_data_hash(&data_hash, &salt)?;
193
194 assert_eq!(pow_direct, pow_split);
195 assert_eq!(hash_direct, hash_split);
196 }
197 Ok(())
198 }
199}