From 8b045cf61c7d166ffe5af5edf63297a98ba447ba Mon Sep 17 00:00:00 2001 From: Andrew Dirksen Date: Thu, 9 May 2019 19:01:11 -0700 Subject: [PATCH] New score semantics. --- Cargo.toml | 3 +++ src/lib.rs | 33 +++++++++++++++++++++++++++++++++ src/proof_of_work.rs | 32 +++++++++++++------------------- 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c000d32..5d07ce1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,9 @@ name = "pow" version = "0.1.0" authors = ["Andrew Dirksen "] +description = """ +Tag arbitrary data types with proof of work based on sha256. +""" edition = "2018" license = "MIT OR Apache-2.0" diff --git a/src/lib.rs b/src/lib.rs index fa096f5..57f1bbe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,36 @@ +//! Sha256 based proof of work over a typed piece of data. +//! +//! Any type that implementes serde::Deserialize can be tagged with a proof of work. +//! +//! # Examples +//! +//! Prove we did work targeting a phrase. +//! +//! ``` +//! use pow::Pow; +//! let difficulty = u128::max_value() / 2; // very easy mode +//! let phrase = b"Phrase to tag.".to_vec(); +//! let pw = Pow::prove_work(&phrase, difficulty).unwrap(); +//! assert!(pw.score(&phrase).unwrap() >= difficulty); +//! ``` +//! +//! Prove more difficult work. This time targeting a time. +//! +//! ``` +//! # fn get_unix_time_seconds() -> u64 { +//! # use std::time::{Duration, SystemTime}; +//! # SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() +//! # } +//! # use pow::Pow; +//! let difficulty = u128::max_value() / 10_000 * 9_999; // more diffcult, around 10_000 hashes +//! let now: u64 = get_unix_time_seconds(); +//! let pw = Pow::prove_work(&now, difficulty).unwrap(); +//! assert!(pw.score(&now).unwrap() >= difficulty); +//! ``` + +//! Expected computional cost scales something like +//! O(u128::max_value() / (u128::max_value() - difficulty)) + mod proof_of_work; pub use proof_of_work::Pow; diff --git a/src/proof_of_work.rs b/src/proof_of_work.rs index 06aa9a3..79ea925 100644 --- a/src/proof_of_work.rs +++ b/src/proof_of_work.rs @@ -4,6 +4,7 @@ use std::marker::PhantomData; const SALT: &'static str = "35af8f4890981391c191e6df45b5f780812ddf0213f29299576ac1c98e18173e"; +/// Proof of work over concrete type T. T can be any type that implements serde::Serialize. #[derive(Serialize, Deserialize, PartialEq, Clone, Copy, Debug)] pub struct Pow { proof: u128, @@ -21,7 +22,7 @@ impl Pow { /// on a general purpose processor. /// /// Returns bincode::Error if serialization fails. - pub fn prove_work(t: &T, difficulty: u32) -> bincode::Result> { + pub fn prove_work(t: &T, difficulty: u128) -> bincode::Result> { bincode_cfg() .serialize(t) .map(|v| Self::prove_work_serialized(&v, difficulty)) @@ -32,8 +33,7 @@ impl Pow { /// /// Make sure difficulty is not too high. A 64 bit difficulty, for example, takes a long time /// on a general purpose processor. - pub fn prove_work_serialized(prefix: &[u8], difficulty: u32) -> Pow { - debug_assert!(difficulty <= 256); + pub fn prove_work_serialized(prefix: &[u8], difficulty: u128) -> Pow { let prefix_sha = Sha256::new().chain(SALT).chain(prefix); let mut n = 0; while score(prefix_sha.clone(), n) < difficulty { @@ -46,7 +46,7 @@ impl Pow { } /// Calculate the pow score of t and self. - pub fn score(&self, t: &T) -> bincode::Result { + pub fn score(&self, t: &T) -> bincode::Result { bincode_cfg() .serialize(t) .map(|v| self.score_serialized(&v)) @@ -54,13 +54,13 @@ impl Pow { /// Calculate the pow score of an already serialized T and self. /// 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]) -> u128 { score(Sha256::new().chain(SALT).chain(target), self.proof) } } -fn score(prefix_sha: Sha256, proof: u128) -> u32 { - leading_zeros( +fn score(prefix_sha: Sha256, proof: u128) -> u128 { + first_bytes_as_u128( prefix_sha .chain(&proof.to_be_bytes()) // to_be_bytes() converts to network endian .fixed_result() @@ -68,17 +68,11 @@ fn score(prefix_sha: Sha256, proof: u128) -> u32 { ) } -fn leading_zeros(inp: &[u8]) -> u32 { - let mut ret = 0; - for n in inp { - if n == &0 { - ret += 8; - } else { - ret += n.leading_zeros(); - break; - } - } - return ret; +/// # Panics +/// +/// panics if inp.len() < 16 +fn first_bytes_as_u128(inp: &[u8]) -> u128 { + bincode_cfg().deserialize(&inp).unwrap() } fn bincode_cfg() -> bincode::Config { @@ -91,7 +85,7 @@ fn bincode_cfg() -> bincode::Config { mod test { use super::*; - const DIFFICULTY: u32 = 10; + const DIFFICULTY: u128 = 0xff000000000000000000000000000000; #[test] fn base_functionality() {