use md5::{Digest as Md5Digest, Md5};
use sha1::Sha1;
use sha2::Sha256;
use super::rand_uid;
#[derive(Debug, Clone)]
pub struct PowResult {
pub pow_msg: String,
pub pow_sign: String,
}
pub fn generate_pow(
lot_number: &str,
captcha_id: &str,
hash_func: &str,
version: &str,
bits: u32,
datetime: &str,
) -> PowResult {
let bit_division = (bits / 4) as usize;
let bit_remainder = bits % 4;
let prefix = "0".repeat(bit_division);
let pow_base = format!(
"{}|{}|{}|{}|{}|{}||",
version, bits, hash_func, datetime, captcha_id, lot_number
);
loop {
let nonce = rand_uid();
let pow_msg = format!("{}{}", pow_base, nonce);
let hash = match hash_func {
"md5" => {
let mut hasher = Md5::new();
hasher.update(pow_msg.as_bytes());
hex::encode(hasher.finalize())
}
"sha1" => {
let mut hasher = Sha1::new();
hasher.update(pow_msg.as_bytes());
hex::encode(hasher.finalize())
}
"sha256" => {
let mut hasher = Sha256::new();
hasher.update(pow_msg.as_bytes());
hex::encode(hasher.finalize())
}
_ => panic!("Unsupported hash function: {}", hash_func),
};
if verify_pow(&hash, &prefix, bit_remainder, bit_division) {
return PowResult {
pow_msg,
pow_sign: hash,
};
}
}
}
fn verify_pow(hash: &str, prefix: &str, bit_remainder: u32, bit_division: usize) -> bool {
if !hash.starts_with(prefix) {
return false;
}
if bit_remainder == 0 {
return true;
}
if let Some(next_char) = hash.chars().nth(bit_division) {
let threshold = match bit_remainder {
1 => '7',
2 => '3',
3 => '1',
_ => return false,
};
return next_char <= threshold;
}
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_pow_zero_bits() {
let result = generate_pow(
"test_lot_number",
"test_captcha_id",
"md5",
"1",
0,
"2025-01-01T00:00:00+00:00",
);
assert!(!result.pow_msg.is_empty());
assert!(!result.pow_sign.is_empty());
assert_eq!(result.pow_sign.len(), 32); }
#[test]
fn test_generate_pow_with_bits() {
let result = generate_pow(
"test_lot_number",
"test_captcha_id",
"md5",
"1",
4,
"2025-01-01T00:00:00+00:00",
);
assert!(result.pow_sign.starts_with('0'));
}
#[test]
fn test_verify_pow() {
assert!(verify_pow("0000abc", "0000", 0, 4));
assert!(!verify_pow("000abc", "0000", 0, 4));
assert!(verify_pow("0007abc", "000", 1, 3)); assert!(!verify_pow("0008abc", "000", 1, 3));
assert!(verify_pow("0003abc", "000", 2, 3)); assert!(!verify_pow("0004abc", "000", 2, 3));
assert!(verify_pow("0001abc", "000", 3, 3)); assert!(!verify_pow("0002abc", "000", 3, 3)); }
}