use std::fmt;
use std::ops::{Add, AddAssign, Div, Sub, SubAssign};
use num_bigint::BigUint;
use beserial::{Deserialize, DeserializeWithLength, ReadBytesExt, Serialize, SerializeWithLength, SerializingError, WriteBytesExt};
use fixed_unsigned::types::FixedUnsigned10;
use hash::Argon2dHash;
use primitives::policy;
#[derive(Default, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize)]
pub struct TargetCompact(u32);
impl fmt::Debug for TargetCompact {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TargetCompact{{{:04x}: shift={}, value={:06x}}}", self.0, self.0 >> 24, self.0 & 0x00FF_FFFF)
}
}
create_typed_array!(Target, u8, 32);
#[derive(Default, Clone, PartialEq, PartialOrd, Eq, Ord, Debug)]
pub struct Difficulty(FixedUnsigned10);
impl From<TargetCompact> for u32 {
fn from(t: TargetCompact) -> Self { t.0 }
}
impl From<u32> for TargetCompact {
fn from(u: u32) -> Self { TargetCompact(u) }
}
impl From<TargetCompact> for Target {
fn from(target_compact: TargetCompact) -> Self {
let mut val = [0u8; 32];
let shift_bytes = (target_compact.0 >> 24).saturating_sub(3) as usize;
val[32 - shift_bytes - 1] = (target_compact.0 & 0xff) as u8;
val[32 - shift_bytes - 2] = ((target_compact.0 >> 8) & 0xff) as u8;
val[32 - shift_bytes - 3] = ((target_compact.0 >> 16) & 0xff) as u8;
Target(val)
}
}
impl From<Target> for TargetCompact {
fn from(target: Target) -> Self {
TargetCompact::from(&target)
}
}
impl<'a> From<&'a Target> for TargetCompact {
fn from(target: &'a Target) -> Self {
let mut first_byte = 0;
for i in 0..target.0.len() {
if target.0[i] > 0 {
first_byte = i;
break;
}
}
if target.0[first_byte] >= 0x80 && first_byte <= 29 {
first_byte -= 1;
}
let shift_bytes = 32 - first_byte;
let start_byte = first_byte.min(29);
TargetCompact(((shift_bytes as u32) << 24)
| (u32::from(target.0[start_byte]) << 16)
| (u32::from(target.0[start_byte + 1]) << 8)
| u32::from(target.0[start_byte + 2]))
}
}
impl<'a> From<&'a Argon2dHash> for Target {
fn from(hash: &'a Argon2dHash) -> Self {
Target::from(hash.as_bytes())
}
}
impl From<BigUint> for Target {
fn from(num: BigUint) -> Self {
let bytes = num.to_bytes_be();
let byte_len = bytes.len();
assert!(byte_len <= 32, "Cannot convert BigDecimal to Target - out of bounds");
let mut target_data = [0u8; 32];
for i in 0..byte_len {
target_data[32 - byte_len + i] = bytes[i];
}
Target(target_data)
}
}
impl From<Target> for BigUint {
fn from(target: Target) -> BigUint {
let target_data: [u8; 32] = target.into();
BigUint::from_bytes_be(&target_data)
}
}
impl From<Target> for FixedUnsigned10 {
fn from(target: Target) -> FixedUnsigned10 {
FixedUnsigned10::from(BigUint::from(target))
}
}
impl From<TargetCompact> for Difficulty {
fn from(target_compact: TargetCompact) -> Self {
Target::from(target_compact).into()
}
}
impl From<Target> for Difficulty {
fn from(target: Target) -> Self {
Difficulty(&*policy::BLOCK_TARGET_MAX / &FixedUnsigned10::from(target))
}
}
impl From<FixedUnsigned10> for Target {
fn from(fixed: FixedUnsigned10) -> Target {
Target::from(fixed.into_biguint())
}
}
impl From<Difficulty> for TargetCompact {
fn from(difficulty: Difficulty) -> Self {
Target::from(difficulty).into()
}
}
impl From<Difficulty> for Target {
fn from(difficulty: Difficulty) -> Self {
Target::from(&*policy::BLOCK_TARGET_MAX / &FixedUnsigned10::from(difficulty))
}
}
impl From<i32> for Difficulty {
fn from(x: i32) -> Difficulty {
warn!("Conversion from `i32` to `Difficulty`. This will panic on negative numbers! Do not use!");
if x < 0 {
panic!("Can't convert a `i32` into a `Difficulty`!");
}
Difficulty(FixedUnsigned10::from(x as u32))
}
}
impl From<FixedUnsigned10> for Difficulty {
fn from(fixed: FixedUnsigned10) -> Self {
Difficulty(fixed)
}
}
impl From<Difficulty> for FixedUnsigned10 {
fn from(difficulty: Difficulty) -> Self { difficulty.0 }
}
impl From<u64> for Difficulty {
fn from(x: u64) -> Difficulty {
Difficulty(FixedUnsigned10::from(x))
}
}
impl From<u32> for Difficulty {
fn from(x: u32) -> Difficulty {
Difficulty(FixedUnsigned10::from(x))
}
}
impl AddAssign<u32> for Difficulty {
fn add_assign(&mut self, rhs: u32) {
*self += Difficulty(FixedUnsigned10::from(rhs));
}
}
impl Div<u32> for Difficulty {
type Output = Difficulty;
fn div(self, rhs: u32) -> <Self as Div<u32>>::Output {
Difficulty(self.0 / FixedUnsigned10::from(rhs))
}
}
impl Add<Difficulty> for Difficulty {
type Output = Difficulty;
fn add(self, rhs: Difficulty) -> Difficulty {
Difficulty(self.0 + rhs.0)
}
}
impl<'a, 'b> Add<&'b Difficulty> for &'a Difficulty {
type Output = Difficulty;
fn add(self, rhs: &'b Difficulty) -> Difficulty {
Difficulty(&self.0 + &rhs.0)
}
}
impl AddAssign<Difficulty> for Difficulty {
fn add_assign(&mut self, rhs: Difficulty) {
*self = Difficulty(&self.0 + &rhs.0);
}
}
impl Sub<Difficulty> for Difficulty {
type Output = Difficulty;
fn sub(self, rhs: Difficulty) -> Difficulty {
Difficulty(self.0 - rhs.0)
}
}
impl<'a, 'b> Sub<&'b Difficulty> for &'a Difficulty {
type Output = Difficulty;
fn sub(self, rhs: &'b Difficulty) -> Difficulty {
Difficulty(&self.0 - &rhs.0)
}
}
impl SubAssign<Difficulty> for Difficulty {
fn sub_assign(&mut self, rhs: Difficulty) {
*self = Difficulty(&self.0 - &rhs.0);
}
}
impl Serialize for Difficulty {
fn serialize<W: WriteBytesExt>(&self, writer: &mut W) -> Result<usize, SerializingError> {
SerializeWithLength::serialize::<u8, W>(&self.0.to_bytes_be(), writer)
}
fn serialized_size(&self) -> usize {
self.0.bytes() + 1 }
}
impl Deserialize for Difficulty {
fn deserialize<R: ReadBytesExt>(reader: &mut R) -> Result<Self, SerializingError> {
let bytes: Vec<u8> = DeserializeWithLength::deserialize::<u8, R>(reader)?;
Ok(Difficulty(FixedUnsigned10::from_bytes_be(bytes.as_slice())))
}
}
impl fmt::Display for Difficulty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
return write!(f, "{}", self.0);
}
}
impl Target {
pub fn is_met_by(&self, hash: &Argon2dHash) -> bool {
let reached = Target::from(hash);
reached < *self
}
pub fn get_depth(&self) -> u8 {
let len = self.0.len();
let mut first_byte = 0;
for i in 0..len {
if self.0[i] > 0 {
first_byte = i;
break;
}
}
let mut last_byte = 0;
for i in 0..len - first_byte {
let idx = len - i - 1;
if self.0[idx] > 0 {
last_byte = idx;
break;
}
}
let leading_zeros = self.0[first_byte].leading_zeros();
let mut exp = 8 - leading_zeros + (len - first_byte - 1) as u32 * 8;
if first_byte == last_byte && self.0[first_byte].trailing_zeros() + leading_zeros == 7 {
exp -= 1;
}
240u8.saturating_sub(exp as u8)
}
}