use crate::{
compute_mask_cerberus,
message::{CerberusBinaryMessage, CerberusDecimalMessage, CerberusMessage},
solver::Solver,
};
use alloc::{format, string::String};
use semver::{BuildMetadata, Prerelease};
#[derive(serde::Deserialize, Debug)]
pub struct ChallengeDescriptor {
challenge: String,
difficulty: core::num::NonZeroU8,
nonce: u64,
ts: u64,
signature: String,
version: MaybeSemver,
}
#[derive(Debug)]
enum MaybeSemver {
Yes(semver::Version),
Unknown,
}
impl<'de> serde::Deserialize<'de> for MaybeSemver {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
match semver::Version::deserialize(deserializer) {
Ok(v) => Ok(MaybeSemver::Yes(v)),
Err(_) => Ok(MaybeSemver::Unknown),
}
}
}
impl ChallengeDescriptor {
pub fn build_msg(&self, working_set: u32) -> Option<CerberusMessage> {
let buf = format!(
"{}|{}|{}|{}|",
self.challenge, self.nonce, self.ts, self.signature
);
const VERSION_FOR_BINARY: semver::Version = semver::Version {
major: 0,
minor: 4,
patch: 6,
pre: Prerelease::EMPTY,
build: BuildMetadata::EMPTY,
};
Some(
if let MaybeSemver::Yes(version) = &self.version
&& *version < VERSION_FOR_BINARY
{
CerberusMessage::Decimal(CerberusDecimalMessage::new(buf.as_bytes(), working_set)?)
} else {
CerberusMessage::Binary(CerberusBinaryMessage::new(buf.as_bytes(), working_set))
},
)
}
pub fn set_version(&mut self, version: semver::Version) {
self.version = MaybeSemver::Yes(version);
}
pub fn estimated_workload(&self) -> u64 {
4u64.saturating_pow(self.difficulty.get().try_into().unwrap())
}
pub fn mask(&self) -> u64 {
compute_mask_cerberus(self.difficulty)
}
pub fn nonce(&self) -> u64 {
self.nonce
}
pub fn ts(&self) -> u64 {
self.ts
}
pub fn signature(&self) -> &str {
&self.signature
}
pub fn solve(&self) -> (Option<(u64, [u32; 8])>, u64) {
self.solve_with_limit(u64::MAX)
}
pub fn solve_with_limit(&self, limit: u64) -> (Option<(u64, [u32; 8])>, u64) {
let mask = compute_mask_cerberus(self.difficulty);
let mut result = None;
let mut attempted_nonces = 0;
let mut remaining_limit = limit;
for search_bank in 0.. {
let Some(message) = self.build_msg(search_bank) else {
break;
};
let mut solver = crate::CerberusSolver::from(message);
solver.set_limit(remaining_limit);
result = solver.solve::<{ crate::solver::SOLVE_TYPE_MASK }>(0, mask);
attempted_nonces += solver.get_attempted_nonces();
remaining_limit = remaining_limit.saturating_sub(solver.get_attempted_nonces());
if result.is_some() || remaining_limit == 0 {
break;
}
}
(result, attempted_nonces)
}
}