use crate::error::{PricingError, Result};
use bincode;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::time::Duration;
pub const DEFAULT_POW_DIFFICULTY: u32 = 20;
pub const DEFAULT_POW_TIME_LIMIT: Duration = Duration::from_secs(10);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Proof {
pub hash: Vec<u8>,
pub nonce: u64,
}
pub async fn generate_proof(challenge: &[u8], difficulty: u32) -> Result<Vec<u8>> {
let mut nonce: u64 = 0;
loop {
let hash = calculate_hash(challenge, nonce);
if check_difficulty(&hash, difficulty) {
let proof = Proof { hash, nonce };
return bincode::serialize(&proof).map_err(PricingError::Serialization);
}
nonce += 1;
if nonce % 1000 == 0 {
tokio::task::yield_now().await;
}
}
}
pub fn verify_proof(challenge: &[u8], proof_bytes: &[u8], difficulty: u32) -> Result<bool> {
let proof: Proof = bincode::deserialize(proof_bytes).map_err(PricingError::Serialization)?;
if !check_difficulty(&proof.hash, difficulty) {
return Ok(false);
}
let recalculated_hash = calculate_hash(challenge, proof.nonce);
Ok(recalculated_hash == proof.hash)
}
fn calculate_hash(challenge: &[u8], nonce: u64) -> Vec<u8> {
let mut hasher = Sha256::new();
hasher.update(challenge);
hasher.update(nonce.to_be_bytes());
hasher.finalize().to_vec()
}
fn check_difficulty(hash: &[u8], difficulty: u32) -> bool {
let zero_bytes = difficulty / 8;
let zero_bits = difficulty % 8;
for i in 0..zero_bytes as usize {
if i >= hash.len() || hash[i] != 0 {
return false;
}
}
if zero_bits > 0 {
let byte_idx = zero_bytes as usize;
if byte_idx >= hash.len() {
return false;
}
let mask = 0xFF << (8 - zero_bits);
if (hash[byte_idx] & mask) != 0 {
return false;
}
}
true
}
pub fn generate_challenge(blueprint_id: u64, timestamp: u64) -> Vec<u8> {
let mut hasher = Sha256::new();
hasher.update(blueprint_id.to_be_bytes());
hasher.update(timestamp.to_be_bytes());
hasher.finalize().to_vec()
}