New score semantics.
This commit is contained in:
parent
bcfd6b49ba
commit
8b045cf61c
3 changed files with 49 additions and 19 deletions
|
@ -2,6 +2,9 @@
|
||||||
name = "pow"
|
name = "pow"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Andrew Dirksen <andrew@dirksen.com>"]
|
authors = ["Andrew Dirksen <andrew@dirksen.com>"]
|
||||||
|
description = """
|
||||||
|
Tag arbitrary data types with proof of work based on sha256.
|
||||||
|
"""
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
|
33
src/lib.rs
33
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;
|
mod proof_of_work;
|
||||||
|
|
||||||
pub use proof_of_work::Pow;
|
pub use proof_of_work::Pow;
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::marker::PhantomData;
|
||||||
|
|
||||||
const SALT: &'static str = "35af8f4890981391c191e6df45b5f780812ddf0213f29299576ac1c98e18173e";
|
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)]
|
#[derive(Serialize, Deserialize, PartialEq, Clone, Copy, Debug)]
|
||||||
pub struct Pow<T: Serialize> {
|
pub struct Pow<T: Serialize> {
|
||||||
proof: u128,
|
proof: u128,
|
||||||
|
@ -21,7 +22,7 @@ impl<T: Serialize> Pow<T> {
|
||||||
/// on a general purpose processor.
|
/// on a general purpose processor.
|
||||||
///
|
///
|
||||||
/// Returns bincode::Error if serialization fails.
|
/// Returns bincode::Error if serialization fails.
|
||||||
pub fn prove_work(t: &T, difficulty: u32) -> bincode::Result<Pow<T>> {
|
pub fn prove_work(t: &T, difficulty: u128) -> bincode::Result<Pow<T>> {
|
||||||
bincode_cfg()
|
bincode_cfg()
|
||||||
.serialize(t)
|
.serialize(t)
|
||||||
.map(|v| Self::prove_work_serialized(&v, difficulty))
|
.map(|v| Self::prove_work_serialized(&v, difficulty))
|
||||||
|
@ -32,8 +33,7 @@ impl<T: Serialize> Pow<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) -> Pow<T> {
|
pub fn prove_work_serialized(prefix: &[u8], difficulty: u128) -> Pow<T> {
|
||||||
debug_assert!(difficulty <= 256);
|
|
||||||
let prefix_sha = Sha256::new().chain(SALT).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 {
|
||||||
|
@ -46,7 +46,7 @@ impl<T: Serialize> Pow<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the pow score of t and self.
|
/// Calculate the pow score of t and self.
|
||||||
pub fn score(&self, t: &T) -> bincode::Result<u32> {
|
pub fn score(&self, t: &T) -> bincode::Result<u128> {
|
||||||
bincode_cfg()
|
bincode_cfg()
|
||||||
.serialize(t)
|
.serialize(t)
|
||||||
.map(|v| self.score_serialized(&v))
|
.map(|v| self.score_serialized(&v))
|
||||||
|
@ -54,13 +54,13 @@ impl<T: Serialize> Pow<T> {
|
||||||
|
|
||||||
/// 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]) -> u128 {
|
||||||
score(Sha256::new().chain(SALT).chain(target), self.proof)
|
score(Sha256::new().chain(SALT).chain(target), self.proof)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn score(prefix_sha: Sha256, proof: u128) -> u32 {
|
fn score(prefix_sha: Sha256, proof: u128) -> u128 {
|
||||||
leading_zeros(
|
first_bytes_as_u128(
|
||||||
prefix_sha
|
prefix_sha
|
||||||
.chain(&proof.to_be_bytes()) // to_be_bytes() converts to network endian
|
.chain(&proof.to_be_bytes()) // to_be_bytes() converts to network endian
|
||||||
.fixed_result()
|
.fixed_result()
|
||||||
|
@ -68,17 +68,11 @@ fn score(prefix_sha: Sha256, proof: u128) -> u32 {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn leading_zeros(inp: &[u8]) -> u32 {
|
/// # Panics
|
||||||
let mut ret = 0;
|
///
|
||||||
for n in inp {
|
/// panics if inp.len() < 16
|
||||||
if n == &0 {
|
fn first_bytes_as_u128(inp: &[u8]) -> u128 {
|
||||||
ret += 8;
|
bincode_cfg().deserialize(&inp).unwrap()
|
||||||
} else {
|
|
||||||
ret += n.leading_zeros();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bincode_cfg() -> bincode::Config {
|
fn bincode_cfg() -> bincode::Config {
|
||||||
|
@ -91,7 +85,7 @@ fn bincode_cfg() -> bincode::Config {
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const DIFFICULTY: u32 = 10;
|
const DIFFICULTY: u128 = 0xff000000000000000000000000000000;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn base_functionality() {
|
fn base_functionality() {
|
||||||
|
|
Loading…
Reference in a new issue