use crate::consensus::{graph_weight, MIN_DMA_DIFFICULTY, SECOND_POW_EDGE_BITS};
use crate::core::hash::{DefaultHashable, Hashed};
use crate::global;
use crate::pow::error::Error;
use crate::ser::{self, DeserializationMode, Readable, Reader, Writeable, Writer};
use rand::{thread_rng, Rng};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::cmp::{max, min};
use std::ops::{Add, Div, Mul, Sub};
use std::u64;
use std::{fmt, iter};
pub trait PoWContext {
fn set_header_nonce(
&mut self,
header: Vec<u8>,
nonce: Option<u32>,
solve: bool,
) -> Result<(), Error>;
fn find_cycles(&mut self) -> Result<Vec<Proof>, Error>;
fn verify(&self, proof: &Proof) -> Result<(), Error>;
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
pub struct Difficulty {
num: u64,
}
impl Difficulty {
pub fn zero() -> Difficulty {
Difficulty { num: 0 }
}
pub fn min_dma() -> Difficulty {
Difficulty {
num: MIN_DMA_DIFFICULTY,
}
}
pub fn min_wtema() -> Difficulty {
Difficulty {
num: global::min_wtema_graph_weight(),
}
}
pub fn unit() -> Difficulty {
Difficulty {
num: global::initial_graph_weight() as u64,
}
}
pub fn from_num(num: u64) -> Difficulty {
Difficulty { num: max(num, 1) }
}
fn from_proof_adjusted(height: u64, proof: &Proof) -> Difficulty {
Difficulty::from_num(proof.scaled_difficulty(graph_weight(height, proof.edge_bits)))
}
fn from_proof_scaled(proof: &Proof, scaling: u32) -> Difficulty {
Difficulty::from_num(proof.scaled_difficulty(scaling as u64))
}
pub fn to_num(self) -> u64 {
self.num
}
}
impl fmt::Display for Difficulty {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.num)
}
}
impl Add<Difficulty> for Difficulty {
type Output = Difficulty;
fn add(self, other: Difficulty) -> Difficulty {
Difficulty {
num: self.num + other.num,
}
}
}
impl Sub<Difficulty> for Difficulty {
type Output = Difficulty;
fn sub(self, other: Difficulty) -> Difficulty {
Difficulty {
num: self.num - other.num,
}
}
}
impl Mul<Difficulty> for Difficulty {
type Output = Difficulty;
fn mul(self, other: Difficulty) -> Difficulty {
Difficulty {
num: self.num * other.num,
}
}
}
impl Div<Difficulty> for Difficulty {
type Output = Difficulty;
fn div(self, other: Difficulty) -> Difficulty {
Difficulty {
num: self.num / other.num,
}
}
}
impl Writeable for Difficulty {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
writer.write_u64(self.num)
}
}
impl Readable for Difficulty {
fn read<R: Reader>(reader: &mut R) -> Result<Difficulty, ser::Error> {
let data = reader.read_u64()?;
Ok(Difficulty { num: data })
}
}
impl Difficulty {
pub const LEN: usize = 8;
}
impl Serialize for Difficulty {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u64(self.num)
}
}
impl<'de> Deserialize<'de> for Difficulty {
fn deserialize<D>(deserializer: D) -> Result<Difficulty, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_u64(DiffVisitor)
}
}
struct DiffVisitor;
impl<'de> de::Visitor<'de> for DiffVisitor {
type Value = Difficulty;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a difficulty")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
let num_in = s.parse::<u64>();
if num_in.is_err() {
return Err(de::Error::invalid_value(
de::Unexpected::Str(s),
&"a value number",
));
};
Ok(Difficulty {
num: num_in.unwrap(),
})
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Difficulty { num: value })
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct ProofOfWork {
pub total_difficulty: Difficulty,
pub secondary_scaling: u32,
pub nonce: u64,
pub proof: Proof,
}
impl Default for ProofOfWork {
fn default() -> ProofOfWork {
let proof_size = global::proofsize();
ProofOfWork {
total_difficulty: Difficulty::min_dma(),
secondary_scaling: 1,
nonce: 0,
proof: Proof::zero(proof_size),
}
}
}
impl Writeable for ProofOfWork {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
if writer.serialization_mode() != ser::SerializationMode::Hash {
self.write_pre_pow(writer)?;
writer.write_u64(self.nonce)?;
}
self.proof.write(writer)?;
Ok(())
}
}
impl Readable for ProofOfWork {
fn read<R: Reader>(reader: &mut R) -> Result<ProofOfWork, ser::Error> {
let total_difficulty = Difficulty::read(reader)?;
let secondary_scaling = reader.read_u32()?;
let nonce = reader.read_u64()?;
let proof = Proof::read(reader)?;
Ok(ProofOfWork {
total_difficulty,
secondary_scaling,
nonce,
proof,
})
}
}
impl ProofOfWork {
pub fn write_pre_pow<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
ser_multiwrite!(
writer,
[write_u64, self.total_difficulty.to_num()],
[write_u32, self.secondary_scaling]
);
Ok(())
}
pub fn to_difficulty(&self, height: u64) -> Difficulty {
if self.proof.edge_bits == SECOND_POW_EDGE_BITS {
Difficulty::from_proof_scaled(&self.proof, self.secondary_scaling)
} else {
Difficulty::from_proof_adjusted(height, &self.proof)
}
}
pub fn to_unscaled_difficulty(&self) -> Difficulty {
Difficulty::from_num(self.proof.scaled_difficulty(1u64))
}
pub fn edge_bits(&self) -> u8 {
self.proof.edge_bits
}
pub fn is_primary(&self) -> bool {
self.proof.edge_bits != SECOND_POW_EDGE_BITS
&& self.proof.edge_bits >= global::min_edge_bits()
}
pub fn is_secondary(&self) -> bool {
self.proof.edge_bits == SECOND_POW_EDGE_BITS
}
}
#[derive(Clone, PartialOrd, PartialEq, Serialize)]
pub struct Proof {
pub edge_bits: u8,
pub nonces: Vec<u64>,
}
impl DefaultHashable for Proof {}
impl fmt::Debug for Proof {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Cuckoo{}(", self.edge_bits)?;
for (i, val) in self.nonces[..].iter().enumerate() {
write!(f, "{:x}", val)?;
if i < self.nonces.len() - 1 {
write!(f, " ")?;
}
}
write!(f, ")")
}
}
impl Eq for Proof {}
impl Proof {
pub fn new(mut in_nonces: Vec<u64>) -> Proof {
in_nonces.sort_unstable();
Proof {
edge_bits: global::min_edge_bits(),
nonces: in_nonces,
}
}
pub fn zero(proof_size: usize) -> Proof {
Proof {
edge_bits: global::min_edge_bits(),
nonces: vec![0; proof_size],
}
}
pub fn pack_len(bit_width: u8) -> usize {
(bit_width as usize * global::proofsize() + 7) / 8
}
pub fn random(proof_size: usize) -> Proof {
let edge_bits = global::min_edge_bits();
let nonce_mask = (1 << edge_bits) - 1;
let mut rng = thread_rng();
let mut v: Vec<u64> = iter::repeat(())
.map(|()| (rng.gen::<u32>() & nonce_mask) as u64)
.take(proof_size)
.collect();
v.sort_unstable();
Proof {
edge_bits: global::min_edge_bits(),
nonces: v,
}
}
pub fn proof_size(&self) -> usize {
self.nonces.len()
}
pub fn pack_nonces(&self) -> Vec<u8> {
let mut compressed = vec![0u8; Proof::pack_len(self.edge_bits)];
pack_bits(
self.edge_bits,
&self.nonces[0..self.nonces.len()],
&mut compressed,
);
compressed
}
fn scaled_difficulty(&self, scale: u64) -> u64 {
let diff = ((scale as u128) << 64) / (max(1, self.hash().to_u64()) as u128);
min(diff, <u64>::max_value() as u128) as u64
}
}
fn pack_bits(bit_width: u8, uncompressed: &[u64], mut compressed: &mut [u8]) {
let mut mini_buffer = 0u64;
let mut remaining = 64;
for el in uncompressed {
mini_buffer |= el << (64 - remaining);
if bit_width < remaining {
remaining -= bit_width;
} else {
compressed[..8].copy_from_slice(&mini_buffer.to_le_bytes());
compressed = &mut compressed[8..];
mini_buffer = el >> remaining;
remaining = 64 + remaining - bit_width;
}
}
let mut remainder = compressed.len() % 8;
if remainder == 0 {
remainder = 8;
}
if mini_buffer > 0 {
compressed[..].copy_from_slice(&mini_buffer.to_le_bytes()[..remainder]);
}
}
fn extract_bits(bits: &[u8], bit_start: usize, bit_count: usize, read_from: usize) -> u64 {
let mut buf: [u8; 8] = [0; 8];
buf.copy_from_slice(&bits[read_from..read_from + 8]);
if bit_count == 64 {
return u64::from_le_bytes(buf);
}
let skip_bits = bit_start - read_from * 8;
let bit_mask = (1 << bit_count) - 1;
u64::from_le_bytes(buf) >> skip_bits & bit_mask
}
fn read_number(bits: &[u8], bit_start: usize, bit_count: usize) -> u64 {
if bit_count == 0 {
return 0;
}
let mut read_from = bit_start / 8;
if read_from + 8 > bits.len() {
read_from = bits.len() - 8;
}
let max_bit_end = (read_from + 8) * 8;
let max_pos = bit_start + bit_count;
if max_pos <= max_bit_end {
extract_bits(bits, bit_start, bit_count, read_from)
} else {
let low = extract_bits(bits, bit_start, 8, read_from);
let high = extract_bits(bits, bit_start + 8, bit_count - 8, read_from + 1);
(high << 8) + low
}
}
impl Readable for Proof {
fn read<R: Reader>(reader: &mut R) -> Result<Proof, ser::Error> {
let edge_bits = reader.read_u8()?;
if edge_bits == 0 || edge_bits > 63 {
return Err(ser::Error::CorruptedData);
}
if reader.deserialization_mode() != DeserializationMode::SkipPow {
let mut nonces = Vec::with_capacity(global::proofsize());
let nonce_bits = edge_bits as usize;
let bytes_len = Proof::pack_len(edge_bits);
if bytes_len < 8 {
return Err(ser::Error::CorruptedData);
}
let bits = reader.read_fixed_bytes(bytes_len)?;
for n in 0..global::proofsize() {
nonces.push(read_number(&bits, n * nonce_bits, nonce_bits));
}
let end_of_data = global::proofsize() * nonce_bits;
if read_number(&bits, end_of_data, bytes_len * 8 - end_of_data) != 0 {
return Err(ser::Error::CorruptedData);
}
Ok(Proof { edge_bits, nonces })
} else {
Ok(Proof {
edge_bits,
nonces: vec![],
})
}
}
}
impl Writeable for Proof {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
if writer.serialization_mode() != ser::SerializationMode::Hash {
writer.write_u8(self.edge_bits)?;
}
writer.write_fixed_bytes(&self.pack_nonces())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ser::{BinReader, BinWriter, DeserializationMode, ProtocolVersion};
use rand::Rng;
use std::io::Cursor;
#[test]
fn test_proof_rw() {
global::set_local_chain_type(global::ChainTypes::Mainnet);
for edge_bits in 10..63 {
let mut proof = Proof::new(gen_proof(edge_bits as u32));
proof.edge_bits = edge_bits;
let mut buf = Cursor::new(Vec::new());
let mut w = BinWriter::new(&mut buf, ProtocolVersion::local());
if let Err(e) = proof.write(&mut w) {
panic!("failed to write proof {:?}", e);
}
buf.set_position(0);
let mut r = BinReader::new(
&mut buf,
ProtocolVersion::local(),
DeserializationMode::default(),
);
match Proof::read(&mut r) {
Err(e) => panic!("failed to read proof: {:?}", e),
Ok(p) => assert_eq!(p, proof),
}
}
}
fn gen_proof(bits: u32) -> Vec<u64> {
let mut rng = rand::thread_rng();
let mut v = Vec::with_capacity(42);
for _ in 0..42 {
v.push(rng.gen_range(
u64::pow(2, bits - 1),
if bits == 64 {
std::u64::MAX
} else {
u64::pow(2, bits)
},
))
}
v
}
}