use crate::primitives::{Hash32, U256};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CompactTarget {
pub target: U256,
pub negative: bool,
pub overflow: bool,
}
impl CompactTarget {
pub fn from_bits(bits: u32) -> Self {
let size = bits >> 24;
let mut word = bits & 0x007f_ffff;
let target = if size <= 3 {
word >>= 8 * (3 - size);
U256::from_u64(word as u64)
} else {
U256::from_u64(word as u64).shl(8 * (size - 3))
};
Self {
target,
negative: word != 0 && bits & 0x0080_0000 != 0,
overflow: word != 0
&& (size > 34 || (word > 0xff && size > 33) || (word > 0xffff && size > 32)),
}
}
pub fn is_valid(&self) -> bool {
!self.negative && !self.overflow && !self.target.is_zero()
}
}
impl U256 {
pub fn to_compact(&self) -> u32 {
self.to_compact_signed(false)
}
pub fn to_compact_signed(&self, negative: bool) -> u32 {
let mut size = self.bits().div_ceil(8);
let mut compact = if size <= 3 {
(self.low_u64() << (8 * (3 - size))) as u32
} else {
self.shr(8 * (size - 3)).low_u64() as u32
};
if compact & 0x0080_0000 != 0 {
compact >>= 8;
size += 1;
}
compact |= size << 24;
if negative && compact & 0x007f_ffff != 0 {
compact |= 0x0080_0000;
}
compact
}
}
pub const POW_LIMIT_MAIN: U256 = U256([u64::MAX, u64::MAX, u64::MAX, 0xffff_ffff]);
pub const POW_LIMIT_TEST: U256 = POW_LIMIT_MAIN;
pub const POW_LIMIT_STN: U256 = POW_LIMIT_MAIN;
pub const POW_LIMIT_REGTEST: U256 = U256([u64::MAX, u64::MAX, u64::MAX, 0x7fff_ffff_ffff_ffff]);
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum PowError {
#[error("negative compact target: {bits:#010x}")]
BitsNegative { bits: u32 },
#[error("zero compact target: {bits:#010x}")]
BitsZero { bits: u32 },
#[error("overflowing compact target: {bits:#010x}")]
BitsOverflow { bits: u32 },
#[error("compact target above proof-of-work limit: {bits:#010x}")]
BitsAboveLimit { bits: u32 },
#[error("hash above compact target: {bits:#010x}")]
HighHash { bits: u32 },
}
impl PowError {
pub fn reject_reason(&self) -> &'static str {
"high-hash"
}
}
pub fn check_proof_of_work(hash: &Hash32, bits: u32, pow_limit: &U256) -> Result<(), PowError> {
let compact = CompactTarget::from_bits(bits);
if compact.negative {
return Err(PowError::BitsNegative { bits });
}
if compact.target.is_zero() {
return Err(PowError::BitsZero { bits });
}
if compact.overflow {
return Err(PowError::BitsOverflow { bits });
}
if compact.target > *pow_limit {
return Err(PowError::BitsAboveLimit { bits });
}
if U256::from_le_bytes(*hash) > compact.target {
return Err(PowError::HighHash { bits });
}
Ok(())
}