use serde and bincode for serialization
This commit is contained in:
parent
16f7b38446
commit
bcfd6b49ba
4 changed files with 55 additions and 232 deletions
|
@ -6,8 +6,6 @@ edition = "2018"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.3.1"
|
|
||||||
sha2 = "0.8.0"
|
sha2 = "0.8.0"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
[dev-dependencies]
|
bincode = "1.1.3"
|
||||||
rand = "0.6.5"
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
mod network_byte_order;
|
|
||||||
mod proof_of_work;
|
mod proof_of_work;
|
||||||
|
|
||||||
pub use proof_of_work::ProofOfWork;
|
pub use proof_of_work::Pow;
|
||||||
|
|
|
@ -1,211 +0,0 @@
|
||||||
// "Ne" is short for Network Endian
|
|
||||||
|
|
||||||
use byteorder::{ByteOrder, NetworkEndian};
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::marker::Sized;
|
|
||||||
use std::mem::size_of;
|
|
||||||
|
|
||||||
/// Serialize to network endian encoded bytes.
|
|
||||||
pub trait ToNe: Sized {
|
|
||||||
/// Write self into dest and return rest, if self is too large to fit in dest, return None
|
|
||||||
fn put<'a>(&self, dest: &'a mut [u8]) -> Option<&'a mut [u8]>;
|
|
||||||
|
|
||||||
/// Returns the size of self when serialized.
|
|
||||||
/// Panics will occur If the return value of this function is too small.
|
|
||||||
fn size(&self) -> usize;
|
|
||||||
|
|
||||||
/// Serialize self to a Vec.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// This function will panic it the size reported by [`size`] is incorrect.
|
|
||||||
fn serialize_to_vec(&self) -> Vec<u8> {
|
|
||||||
let mut ret = vec![0u8; self.size()];
|
|
||||||
let rest = self
|
|
||||||
.put(&mut ret)
|
|
||||||
.expect("object serialized was larger than reported");
|
|
||||||
if !rest.is_empty() {
|
|
||||||
panic!("object serialized was smaller than reported");
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize to/from network endian encoded bytes.
|
|
||||||
pub trait Ne: ToNe {
|
|
||||||
/// Parse bytes as network endian. Return parsed value and unparsed bytes.
|
|
||||||
/// If src is not long enough, return None.
|
|
||||||
fn pick(src: &[u8]) -> Option<(Self, &[u8])>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToNe for u128 {
|
|
||||||
fn put<'a>(&self, dest: &'a mut [u8]) -> Option<&'a mut [u8]> {
|
|
||||||
let (mut head, rest) = safe_split_mut(dest, size_of::<Self>())?;
|
|
||||||
NetworkEndian::write_u128(&mut head, *self);
|
|
||||||
Some(rest)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size(&self) -> usize {
|
|
||||||
size_of::<Self>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ne for u128 {
|
|
||||||
fn pick(src: &[u8]) -> Option<(Self, &[u8])> {
|
|
||||||
let (head, rest) = take_sized::<[u8; 16]>(src)?;
|
|
||||||
Some((NetworkEndian::read_u128(&head), rest))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ToNe> ToNe for &T {
|
|
||||||
fn put<'a>(&self, dest: &'a mut [u8]) -> Option<&'a mut [u8]> {
|
|
||||||
(*self).put(dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size(&self) -> usize {
|
|
||||||
(*self).size()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ToNe, S: ToNe> ToNe for (T, S) {
|
|
||||||
fn put<'a>(&self, dest: &'a mut [u8]) -> Option<&'a mut [u8]> {
|
|
||||||
let (t, s) = self;
|
|
||||||
let dest = t.put(dest)?;
|
|
||||||
let dest = s.put(dest)?;
|
|
||||||
Some(dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size(&self) -> usize {
|
|
||||||
let (t, s) = self;
|
|
||||||
ToNe::size(t) + ToNe::size(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Ne, S: Ne> Ne for (T, S) {
|
|
||||||
fn pick(src: &[u8]) -> Option<(Self, &[u8])> {
|
|
||||||
let (t, src) = T::pick(src)?;
|
|
||||||
let (s, src) = S::pick(src)?;
|
|
||||||
Some(((t, s), src))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: ToNe, B: ToNe, C: ToNe, D: ToNe> ToNe for (A, B, C, D) {
|
|
||||||
fn put<'a>(&self, dest: &'a mut [u8]) -> Option<&'a mut [u8]> {
|
|
||||||
let (a, b, c, d) = self;
|
|
||||||
let dest = a.put(dest)?;
|
|
||||||
let dest = b.put(dest)?;
|
|
||||||
let dest = c.put(dest)?;
|
|
||||||
let dest = d.put(dest)?;
|
|
||||||
Some(dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size(&self) -> usize {
|
|
||||||
let (a, b, c, d) = self;
|
|
||||||
ToNe::size(a) + ToNe::size(b) + ToNe::size(c) + ToNe::size(d)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: Ne, B: Ne, C: Ne, D: Ne> Ne for (A, B, C, D) {
|
|
||||||
fn pick(src: &[u8]) -> Option<(Self, &[u8])> {
|
|
||||||
let (a, src) = A::pick(src)?;
|
|
||||||
let (b, src) = B::pick(src)?;
|
|
||||||
let (c, src) = C::pick(src)?;
|
|
||||||
let (d, src) = D::pick(src)?;
|
|
||||||
Some(((a, b, c, d), src))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToNe for Vec<u8> {
|
|
||||||
fn put<'a>(&self, dest: &'a mut [u8]) -> Option<&'a mut [u8]> {
|
|
||||||
put(self.as_ref(), dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size(&self) -> usize {
|
|
||||||
self.len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ne for Vec<u8> {
|
|
||||||
fn pick(src: &[u8]) -> Option<(Self, &[u8])> {
|
|
||||||
Some((src.to_vec(), &[]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Split src at n index or None if src.len() < n.
|
|
||||||
fn safe_split(src: &[u8], n: usize) -> Option<(&[u8], &[u8])> {
|
|
||||||
if src.len() >= n {
|
|
||||||
Some(src.split_at(n))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Split src at n index or None if src.len() < n.
|
|
||||||
fn safe_split_mut(src: &mut [u8], n: usize) -> Option<(&mut [u8], &mut [u8])> {
|
|
||||||
if src.len() >= n {
|
|
||||||
Some(src.split_at_mut(n))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Split src on at n index or None if src.len() < n.
|
|
||||||
fn take_sized<'a, T>(src: &'a [u8]) -> Option<(T, &'a [u8])>
|
|
||||||
where
|
|
||||||
&'a [u8]: TryInto<T>,
|
|
||||||
{
|
|
||||||
let (head, tail) = safe_split(src, size_of::<T>())?;
|
|
||||||
let ret = head.try_into().ok();
|
|
||||||
debug_assert!(ret.is_some());
|
|
||||||
Some((ret?, tail))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write src into dest, return unwriten bytes or None if dest is not long enough.
|
|
||||||
fn put<'a>(src: &[u8], dest: &'a mut [u8]) -> Option<(&'a mut [u8])> {
|
|
||||||
let (head, tail) = safe_split_mut(dest, src.len())?;
|
|
||||||
head.copy_from_slice(src);
|
|
||||||
Some(tail)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use rand::random;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
fn ser<T: ToNe>(t: T) {
|
|
||||||
t.serialize_to_vec();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ser_deser<T: Ne + PartialEq + Debug>(t: T) {
|
|
||||||
let v = t.serialize_to_vec();
|
|
||||||
let (t2, rest) = T::pick(&v).unwrap();
|
|
||||||
assert_eq!(rest.len(), 0);
|
|
||||||
assert_eq!(t, t2);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rand_vecu8() -> Vec<u8> {
|
|
||||||
(0..(random::<usize>() % 265)).map(|_| random()).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sd_u128() {
|
|
||||||
ser_deser::<u128>(random());
|
|
||||||
ser::<&u128>(&random());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sd_2t() {
|
|
||||||
ser_deser::<(u128, u128)>(random());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sd_4t() {
|
|
||||||
ser_deser::<(u128, u128, u128, u128)>(random());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sd_vu8() {
|
|
||||||
ser_deser(rand_vecu8());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +1,30 @@
|
||||||
use crate::network_byte_order::Ne;
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::{digest::FixedOutput, Digest, Sha256};
|
use sha2::{digest::FixedOutput, Digest, Sha256};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
pub struct ProofOfWork<T: Ne> {
|
const SALT: &'static str = "35af8f4890981391c191e6df45b5f780812ddf0213f29299576ac1c98e18173e";
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, PartialEq, Clone, Copy, Debug)]
|
||||||
|
pub struct Pow<T: Serialize> {
|
||||||
proof: u128,
|
proof: u128,
|
||||||
_spook: PhantomData<T>,
|
_spook: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Ne> ProofOfWork<T> {
|
// prove_work and score could theoretically be without allocations, by serializing to a Write
|
||||||
|
// implementaion that performs sha256 lazily.
|
||||||
|
// `impl io::Write for sha2::Sha256 { ... }`
|
||||||
|
|
||||||
|
impl<T: Serialize> Pow<T> {
|
||||||
/// Prove work over T.
|
/// Prove work over T.
|
||||||
///
|
///
|
||||||
/// Make sure difficulty is not too high. A 64 bit difficulty, for example, takes a long time
|
/// Make sure difficulty is not too high. A 64 bit difficulty, for example, takes a long time
|
||||||
/// on a general purpose processor.
|
/// on a general purpose processor.
|
||||||
pub fn prove_work(t: &T, difficulty: u32) -> ProofOfWork<T> {
|
///
|
||||||
let v = t.serialize_to_vec();
|
/// Returns bincode::Error if serialization fails.
|
||||||
Self::prove_work_serialized(&v, difficulty)
|
pub fn prove_work(t: &T, difficulty: u32) -> bincode::Result<Pow<T>> {
|
||||||
|
bincode_cfg()
|
||||||
|
.serialize(t)
|
||||||
|
.map(|v| Self::prove_work_serialized(&v, difficulty))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prove work on an already serialized item of type T.
|
/// Prove work on an already serialized item of type T.
|
||||||
|
@ -22,29 +32,30 @@ impl<T: Ne> ProofOfWork<T> {
|
||||||
///
|
///
|
||||||
/// Make sure difficulty is not too high. A 64 bit difficulty, for example, takes a long time
|
/// Make sure difficulty is not too high. A 64 bit difficulty, for example, takes a long time
|
||||||
/// on a general purpose processor.
|
/// on a general purpose processor.
|
||||||
pub fn prove_work_serialized(prefix: &[u8], difficulty: u32) -> ProofOfWork<T> {
|
pub fn prove_work_serialized(prefix: &[u8], difficulty: u32) -> Pow<T> {
|
||||||
debug_assert!(difficulty <= 256);
|
debug_assert!(difficulty <= 256);
|
||||||
let prefix_sha = Sha256::new().chain(prefix);
|
let prefix_sha = Sha256::new().chain(SALT).chain(prefix);
|
||||||
let mut n = 0;
|
let mut n = 0;
|
||||||
while score(prefix_sha.clone(), n) < difficulty {
|
while score(prefix_sha.clone(), n) < difficulty {
|
||||||
n += 1;
|
n += 1;
|
||||||
}
|
}
|
||||||
ProofOfWork {
|
Pow {
|
||||||
proof: n,
|
proof: n,
|
||||||
_spook: PhantomData,
|
_spook: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the pow score of t and self.
|
/// Calculate the pow score of t and self.
|
||||||
pub fn score(&self, t: &T) -> u32 {
|
pub fn score(&self, t: &T) -> bincode::Result<u32> {
|
||||||
let v = t.serialize_to_vec();
|
bincode_cfg()
|
||||||
self.score_serialized(&v)
|
.serialize(t)
|
||||||
|
.map(|v| self.score_serialized(&v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the pow score of an already serialized T and self.
|
/// Calculate the pow score of an already serialized T and self.
|
||||||
/// The input is assumed to be serialized using network byte order.
|
/// The input is assumed to be serialized using network byte order.
|
||||||
pub fn score_serialized(&self, target: &[u8]) -> u32 {
|
pub fn score_serialized(&self, target: &[u8]) -> u32 {
|
||||||
score(Sha256::new().chain(target), self.proof)
|
score(Sha256::new().chain(SALT).chain(target), self.proof)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +81,12 @@ fn leading_zeros(inp: &[u8]) -> u32 {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bincode_cfg() -> bincode::Config {
|
||||||
|
let mut cfg = bincode::config();
|
||||||
|
cfg.big_endian();
|
||||||
|
cfg
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -80,7 +97,27 @@ mod test {
|
||||||
fn base_functionality() {
|
fn base_functionality() {
|
||||||
// Let's prove we did work targeting a phrase.
|
// Let's prove we did work targeting a phrase.
|
||||||
let phrase = b"Corver bandar palladianism retroform.".to_vec();
|
let phrase = b"Corver bandar palladianism retroform.".to_vec();
|
||||||
let pw = ProofOfWork::prove_work(&phrase, DIFFICULTY);
|
let pw = Pow::prove_work(&phrase, DIFFICULTY).unwrap();
|
||||||
assert!(pw.score(&phrase) >= DIFFICULTY);
|
assert!(pw.score(&phrase).unwrap() >= DIFFICULTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn double_pow() {
|
||||||
|
let phrase = "Corver bandar palladianism retroform.".to_owned();
|
||||||
|
let pow = Pow::prove_work(&phrase, DIFFICULTY).unwrap();
|
||||||
|
let powpow: Pow<Pow<String>> = Pow::prove_work(&pow, DIFFICULTY).unwrap();
|
||||||
|
assert!(pow.score(&phrase).unwrap() >= DIFFICULTY);
|
||||||
|
assert!(powpow.score(&pow).unwrap() >= DIFFICULTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ser_de() {
|
||||||
|
let target: u8 = 1;
|
||||||
|
let pw = Pow::prove_work(&target, DIFFICULTY).unwrap();
|
||||||
|
let message: (u8, Pow<u8>) = (target, pw);
|
||||||
|
let message_ser = bincode_cfg().serialize(&message).unwrap();
|
||||||
|
let recieved_message: (u8, Pow<u8>) = bincode_cfg().deserialize(&message_ser).unwrap();
|
||||||
|
assert_eq!(recieved_message, message);
|
||||||
|
assert!(message.1.score(&message.0).unwrap() >= DIFFICULTY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue