use crate::pow::v1::challenge::Challenge;
use crate::pow::v1::{
err::RuntimeErrorV1, types::Effort, types::Instance, types::NONCE_LEN, types::Nonce,
types::Solution,
};
use equix::{EquiXBuilder, HashError, RuntimeOption, SolverMemory};
use rand::{CryptoRng, Rng, RngCore};
#[derive(Debug, Clone)]
pub struct SolverInput {
instance: Instance,
effort: Effort,
equix: EquiXBuilder,
}
impl SolverInput {
pub fn new(instance: Instance, effort: Effort) -> Self {
SolverInput {
instance,
effort,
equix: Default::default(),
}
}
pub fn runtime(&mut self, option: RuntimeOption) -> &mut Self {
self.equix.runtime(option);
self
}
pub fn solve<R: RngCore + CryptoRng>(self, rng: &mut R) -> Solver {
self.solve_with_nonce(&rng.random::<[u8; NONCE_LEN]>().into())
}
pub fn solve_with_nonce(self, nonce: &Nonce) -> Solver {
Solver {
challenge: Challenge::new(&self.instance, self.effort, nonce),
equix: self.equix,
mem: SolverMemory::new(),
}
}
}
pub struct Solver {
challenge: Challenge,
equix: EquiXBuilder,
mem: SolverMemory,
}
impl Solver {
pub fn run(&mut self) -> Result<Solution, RuntimeErrorV1> {
loop {
if let Some(solution) = self.run_step()? {
return Ok(solution);
}
}
}
pub fn run_step(&mut self) -> Result<Option<Solution>, RuntimeErrorV1> {
match self.equix.build(self.challenge.as_ref()) {
Ok(equix) => {
for candidate in equix.solve_with_memory(&mut self.mem) {
if self.challenge.check_effort(&candidate.to_bytes()).is_ok() {
return Ok(Some(Solution::new(
self.challenge.nonce(),
self.challenge.effort(),
self.challenge.seed().head(),
candidate,
)));
}
}
}
Err(equix::Error::Hash(HashError::ProgramConstraints)) => (),
Err(e) => {
return Err(e.into());
}
};
self.challenge.increment_nonce();
Ok(None)
}
}