use crypto::{
encoding::ternary::{b1t6, Btrit, T1B1Buf, TritBuf, Trits, T1B1},
hashes::{
blake2b::Blake2b256,
ternary::{curl_p::CurlP, HASH_LENGTH},
Digest,
},
};
pub struct PowScorer {
blake2b: Blake2b256,
pow_input: TritBuf<T1B1Buf>,
curl: CurlP,
}
impl Default for PowScorer {
fn default() -> Self {
Self::new()
}
}
impl PowScorer {
pub fn new() -> Self {
Self {
blake2b: Blake2b256::new(),
pow_input: TritBuf::<T1B1Buf>::with_capacity(HASH_LENGTH),
curl: CurlP::new(),
}
}
pub fn hash(&mut self, bytes: &[u8]) -> TritBuf<T1B1Buf> {
debug_assert!(bytes.len() >= std::mem::size_of::<u8>());
let length = bytes.len() - std::mem::size_of::<u64>();
let (head, tail) = bytes.split_at(length);
self.blake2b.update(head);
let pow_digest = self.blake2b.finalize_reset();
self.pow_input.clear();
self.pow_input.append(b1t6::encode::<T1B1Buf>(&pow_digest).as_slice());
self.pow_input.append(b1t6::encode::<T1B1Buf>(tail).as_slice());
self.pow_input.push(Btrit::Zero);
self.pow_input.push(Btrit::Zero);
self.pow_input.push(Btrit::Zero);
self.curl.digest(self.pow_input.as_slice())
}
pub fn score(&mut self, bytes: &[u8]) -> f64 {
pow_score_for_hash(&self.hash(bytes), bytes.len())
}
}
pub fn count_trailing_zeros(pow_hash: &Trits<T1B1>) -> usize {
pow_hash.iter().rev().take_while(|t| *t == Btrit::Zero).count()
}
pub fn pow_score_for_hash(pow_hash: &Trits<T1B1>, len: usize) -> f64 {
3u128.pow(count_trailing_zeros(pow_hash) as u32) as f64 / len as f64
}