use crypto::{
encoding::ternary::{b1t6, T1B1Buf, TritBuf},
hashes::{
blake2b::Blake2b256,
ternary::{
curl_p::{CurlPBatchHasher, BATCH_SIZE},
HASH_LENGTH,
},
Digest,
},
};
use super::{score::count_trailing_zeros, LN_3};
const POW_ROUNDS_BEFORE_INTERVAL_CHECK: usize = 3000;
#[derive(Default)]
#[must_use]
pub struct SingleThreadedMinerBuilder {
timeout_in_seconds: Option<u64>,
}
impl SingleThreadedMinerBuilder {
pub fn new() -> Self {
Self { ..Default::default() }
}
pub fn with_timeout_in_seconds(mut self, timeout_in_seconds: impl Into<Option<u64>>) -> Self {
self.timeout_in_seconds = timeout_in_seconds.into();
self
}
pub fn finish(self) -> SingleThreadedMiner {
SingleThreadedMiner {
timeout_in_seconds: self
.timeout_in_seconds
.map(|timeout| instant::Duration::from_secs(timeout)),
}
}
}
pub struct SingleThreadedMiner {
timeout_in_seconds: Option<instant::Duration>,
}
impl SingleThreadedMiner {
pub fn nonce(&self, bytes: &[u8], target_score: u32) -> Option<u64> {
let mut nonce = 0;
let mut pow_digest = TritBuf::<T1B1Buf>::new();
let target_zeros =
(((bytes.len() + std::mem::size_of::<u64>()) as f64 * target_score as f64).ln() / LN_3).ceil() as usize;
let mut hasher = CurlPBatchHasher::<T1B1Buf>::new(HASH_LENGTH);
let mut buffers = Vec::<TritBuf<T1B1Buf>>::with_capacity(BATCH_SIZE);
let hash = Blake2b256::digest(bytes);
b1t6::encode::<T1B1Buf>(&hash).iter().for_each(|t| pow_digest.push(t));
for _ in 0..BATCH_SIZE {
let mut buffer = TritBuf::<T1B1Buf>::zeros(HASH_LENGTH);
buffer[..pow_digest.len()].copy_from(&pow_digest);
buffers.push(buffer);
}
let mut counter = 0;
let mining_start = instant::Instant::now();
loop {
if let Some(timeout) = self.timeout_in_seconds {
if counter % POW_ROUNDS_BEFORE_INTERVAL_CHECK == 0 && mining_start.elapsed() > timeout {
break;
}
}
for (i, buffer) in buffers.iter_mut().enumerate() {
let nonce_trits = b1t6::encode::<T1B1Buf>(&(nonce + i as u64).to_le_bytes());
buffer[pow_digest.len()..pow_digest.len() + nonce_trits.len()].copy_from(&nonce_trits);
hasher.add(buffer.clone());
}
for (i, hash) in hasher.hash().enumerate() {
if count_trailing_zeros(&hash) >= target_zeros {
return Some(nonce + i as u64);
}
}
nonce += BATCH_SIZE as u64;
counter += 1;
}
None
}
}