Updated naming and minor details on several things

This commit is contained in:
Robert Kornacki 2019-07-22 13:54:13 -04:00
parent c1ec5d2a68
commit 1f1b1ade27
5 changed files with 153 additions and 154 deletions

View file

@ -1,19 +1,18 @@
[package]
name = "pow"
version = "0.1.5"
authors = ["Andrew Dirksen <andrew@dirksen.com>"]
name = "PoW-SHA256"
version = "0.1.0"
authors = ["Robert Kornacki <robk@syre.io>"]
description = """
Generate or verify sha256 based proofs of work over arbitrary typed data.
SHA256 PoW on any serializable datatype.
"""
edition = "2018"
documentation = "https://docs.rs/pow"
keywords = ["pwset", "powtag", "tag"]
# documentation = "https://docs.rs/PoW"
keywords = ["PoW", "sha256", "proof-of-work"]
readme = "readme.md"
license = "MIT OR Apache-2.0"
repository = "https://github.com/bddap/pow"
repository = "https://github.com/robkorn/pow"
categories = [] # pr me!
[dependencies]
sha2 = "0.8.0"
serde = { version = "1", features = ["derive"] }
bincode = "1.1.3"
bincode = "1.1.4"

View file

@ -15,7 +15,7 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
"control" means (i) the PoWer, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.

View file

@ -1,4 +1,4 @@
# Pow
# PoW
Sha256 based proof of work over a typed piece of data.
@ -9,13 +9,13 @@ Any type that implementes serde::Deserialize can be tagged with a proof of work.
Prove we did work targeting a phrase.
```rust
use pow::Pow;
use PoW::PoW;
// very easy mode
let difficulty = u128::max_value() - u128::max_value() / 2;
let phrase = b"Phrase to tag.".to_vec();
let pw = Pow::prove_work(&phrase, difficulty).unwrap();
let pw = PoW::prove_work(&phrase, difficulty).unwrap();
assert!(pw.score(&phrase).unwrap() >= difficulty);
```
@ -26,7 +26,7 @@ Prove more difficult work. This time targeting a time.
let difficulty = u128::max_value() - u128::max_value() / 100_000;
let now: u64 = get_unix_time_seconds();
let pw = Pow::prove_work(&now, difficulty).unwrap();
let pw = PoW::prove_work(&now, difficulty).unwrap();
assert!(pw.score(&now).unwrap() >= difficulty);
```
@ -36,24 +36,24 @@ Define a blockchain block.
struct Block<T> {
prev: [u8; 32], // hash of last block
payload: T, // generic data
proof_of_work: Pow<([u8; 32], T)>,
proof_of_work: PoW<([u8; 32], T)>,
}
```
# Score scheme
To score a proof of work for a given (target, Pow) pair:
Sha256 is calculated over the concatenation SALT + target + Pow.
To score a proof of work for a given (target, PoW) pair:
Sha256 is calculated over the concatenation SALT + target + PoW.
The first 16 bytes of the hash are interpreted as a 128 bit unsigned integer.
That integer is the score.
A constant, SALT, is used as prefix to prevent pow reuse from other systems such as proof
A constant, SALT, is used as prefix to prevent PoW reuse from other systems such as proof
of work blockchains.
In other words:
```rust
fn score<T: Serialize>(target: &T, pow_tag: &Pow<T>) -> u128 {
let bytes = serialize(&SALT) + serialize(target) + serialize(pow_tag);
fn score<T: Serialize>(target: &T, PoW_tag: &PoW<T>) -> u128 {
let bytes = serialize(&SALT) + serialize(target) + serialize(PoW_tag);
let hash = sha256(&bytes);
deserialize(&hash[..16])
}
@ -66,7 +66,7 @@ deterministic serialization. All values are serialized using network byte order.
# Threshold scheme
Given a minimum score m. A Pow p satisfies the minimum score for target t iff score(t, p) >= m.
Given a minimum score m. A PoW p satisfies the minimum score for target t iff score(t, p) >= m.
# Choosing a difficulty setting.

View file

@ -1,112 +1,112 @@
//! 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.
//!
//! ```rust
//! use pow::Pow;
//!
//! // very easy mode
//! let difficulty = u128::max_value() - u128::max_value() / 2;
//!
//! 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.
//!
//! ```rust
//! # fn get_unix_time_seconds() -> u64 {
//! # use std::time::{Duration, SystemTime};
//! # SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs()
//! # }
//! # use pow::Pow;
//!
//! // more diffcult, takes around 100_000 hashes to generate proof
//! let difficulty = u128::max_value() - u128::max_value() / 100_000;
//!
//! let now: u64 = get_unix_time_seconds();
//! let pw = Pow::prove_work(&now, difficulty).unwrap();
//! assert!(pw.score(&now).unwrap() >= difficulty);
//! ```
//!
//! Define a blockchain block.
//!
//! ```rust
//! # use pow::Pow;
//! struct Block<T> {
//! prev: [u8; 32], // hash of last block
//! payload: T, // generic data
//! proof_of_work: Pow<([u8; 32], T)>,
//! }
//! ```
//!
//! # Score scheme
//!
//! To score a proof of work for a given (target, Pow) pair:
//! Sha256 is calculated over the concatenation SALT + target + Pow.
//! The first 16 bytes of the hash are interpreted as a 128 bit unsigned integer.
//! That integer is the score.
//! A constant, SALT, is used as prefix to prevent pow reuse from other systems such as proof
//! of work blockchains.
//!
//! In other words:
//!
//! ```rust
//! # use serde::Serialize;
//! # use pow::Pow;
//! # use core::any::Any;
//! # const SALT: &'static str = "not the actual salt used";
//! # fn serialize<T: Serialize>(_: &T) -> u8 { 0 } // not the actual serialize function
//! # fn deserialize(_: &[u8]) -> u128 { 0 } // not the actual deserialize function
//! # fn sha256(_: &u8) -> [u8; 32] { [0; 32] } // not the actual sha256 function
//! fn score<T: Serialize>(target: &T, pow_tag: &Pow<T>) -> u128 {
//! let bytes = serialize(&SALT) + serialize(target) + serialize(pow_tag);
//! let hash = sha256(&bytes);
//! deserialize(&hash[..16])
//! }
//! ```
//!
//! # Serialization encoding.
//!
//! It shouldn't matter to users of this library, but the bincode crate is used for cheap
//! deterministic serialization. All values are serialized using network byte order.
//!
//! # Threshold scheme
//!
//! Given a minimum score m. A Pow p satisfies the minimum score for target t iff score(t, p) >= m.
//!
//! # Choosing a difficulty setting.
//!
//! Difficulty settings are usually best adjusted dynamically a la bitcoin.
//!
//! To manually select a difficulty, choose the average number of hashes required.
//!
//! ```rust
//! fn difficulty(average: u128) -> u128 {
//! debug_assert_ne!(average, 0, "It is impossible to prove work in zero attempts.");
//! let m = u128::max_value();
//! m - m / average
//! }
//! ```
//!
//! Conversely, to calculate probable number of hashes required to satisfy a given minimum
//! difficulty.
//!
//! ```rust
//! fn average(difficulty: u128) -> u128 {
//! let m = u128::max_value();
//! if difficulty == m {
//! return m;
//! }
//! m / (m - difficulty)
//! }
//! ```
// 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.
// ```rust
// use PoW::PoW;
// // very easy mode
// let difficulty = u128::max_value() - u128::max_value() / 2;
// let phrase = b"Phrase to tag.".to_vec();
// let pw = PoW::prove_work(&phrase, difficulty).unwrap();
// assert!(pw.calculate(&phrase).unwrap() >= difficulty);
// ```
// Prove more difficult work. This time targeting a time.
// ```rust
// # fn get_unix_time_seconds() -> u64 {
// # use std::time::{Duration, SystemTime};
// # SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs()
// # }
// # use PoW::PoW;
// // more diffcult, takes around 100_000 hashes to generate proof
// let difficulty = u128::max_value() - u128::max_value() / 100_000;
// let now: u64 = get_unix_time_seconds();
// let pw = PoW::prove_work(&now, difficulty).unwrap();
// assert!(pw.calculate(&now).unwrap() >= difficulty);
// ```
// Define a blockchain block.
// ```rust
// # use PoW::PoW;
// struct Block<T> {
// prev: [u8; 32], // hash of last block
// payload: T, // generic data
// proof_of_work: PoW<([u8; 32], T)>,
// }
// ```
// # Score scheme
// To score a proof of work for a given (target, PoW) pair:
// Sha256 is calculated over the concatenation SALT + target + PoW.
// The first 16 bytes of the hash are interpreted as a 128 bit unsigned integer.
// That integer is the score.
// A constant, SALT, is used as prefix to prevent PoW reuse from other systems such as proof
// of work blockchains.
// In other words:
// ```rust
// # use serde::Serialize;
// # use PoW::PoW;
// # use core::any::Any;
// # const SALT: &'static str = "not the actual salt used";
// # fn serialize<T: Serialize>(_: &T) -> u8 { 0 } // not the actual serialize function
// # fn deserialize(_: &[u8]) -> u128 { 0 } // not the actual deserialize function
// # fn sha256(_: &u8) -> [u8; 32] { [0; 32] } // not the actual sha256 function
// fn score<T: Serialize>(target: &T, PoW_tag: &PoW<T>) -> u128 {
// let bytes = serialize(&SALT) + serialize(target) + serialize(PoW_tag);
// let hash = sha256(&bytes);
// deserialize(&hash[..16])
// }
// ```
// # Serialization encoding.
// It shouldn't matter to users of this library, but the bincode crate is used for cheap
// deterministic serialization. All values are serialized using network byte order.
// # Threshold scheme
// Given a minimum score m. A PoW p satisfies the minimum score for target t iff score(t, p) >= m.
// # Choosing a difficulty setting.
// Difficulty settings are usually best adjusted dynamically a la bitcoin.
// To manually select a difficulty, choose the average number of hashes required.
// ```rust
// fn difficulty(average: u128) -> u128 {
// debug_assert_ne!(average, 0, "It is impossible to prove work in zero attempts.");
// let m = u128::max_value();
// m - m / average
// }
// ```
// Conversely, to calculate probable number of hashes required to satisfy a given minimum
// difficulty.
// ```rust
// fn average(difficulty: u128) -> u128 {
// let m = u128::max_value();
// if difficulty == m {
// return m;
// }
// m / (m - difficulty)
// }
// ```
mod proof_of_work;
pub use proof_of_work::Pow;
pub use proof_of_work::PoW;

View file

@ -4,37 +4,37 @@ use std::marker::PhantomData;
const SALT: &str = "79ziepia7vhjgviiwjhnend3ofjqocsi2winc4ptqhmkvcajihywxcizewvckg9h6gs4j83v9";
/// Proof of work over concrete type T. T can be any type that implements serde::Serialize.
/// 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<T> {
pub struct PoW<T> {
nonce: u128,
result: u128,
_spook: PhantomData<T>,
}
// prove_work and score could theoretically be without allocations, by serializing to a Write
// prove_work and calculate 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.
impl<T: Serialize> PoW<T> {
/// Create Proof of Work over item of type T.
///
/// Make sure difficulty is not too high. A 64 bit difficulty, for example, takes a long time
/// on a general purpose processor.
///
/// Returns bincode::Error if serialization fails.
pub fn prove_work(t: &T, difficulty: u128) -> bincode::Result<Pow<T>> {
pub fn prove_work(t: &T, difficulty: u128) -> 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.
/// Create Proof of Work on an already serialized item of type T.
/// The input is assumed to be serialized using network byte order.
///
/// 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: u128) -> Pow<T> {
pub fn prove_work_serialized(prefix: &[u8], difficulty: u128) -> PoW<T> {
let prefix_sha = Sha256::new().chain(SALT).chain(prefix);
let mut n = 0;
let mut result = 0;
@ -42,23 +42,23 @@ impl<T: Serialize> Pow<T> {
n += 1;
result = score(prefix_sha.clone(), n);
}
Pow {
PoW {
nonce: n,
result: result,
_spook: PhantomData,
}
}
/// Calculate the pow score of t and self.
pub fn score(&self, t: &T) -> bincode::Result<u128> {
/// Calculate the PoW score of t and self.
pub fn calculate(&self, t: &T) -> bincode::Result<u128> {
bincode_cfg()
.serialize(t)
.map(|v| self.score_serialized(&v))
.map(|v| self.calculate_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.
pub fn score_serialized(&self, target: &[u8]) -> u128 {
pub fn calculate_serialized(&self, target: &[u8]) -> u128 {
score(Sha256::new().chain(SALT).chain(target), self.nonce)
}
}
@ -95,27 +95,27 @@ mod test {
fn base_functionality() {
// Let's prove we did work targeting a phrase.
let phrase = b"Corver bandar palladianism retroform.".to_vec();
let pw = Pow::prove_work(&phrase, DIFFICULTY).unwrap();
assert!(pw.score(&phrase).unwrap() >= DIFFICULTY);
let pw = PoW::prove_work(&phrase, DIFFICULTY).unwrap();
assert!(pw.calculate(&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);
let PoW = PoW::prove_work(&phrase, DIFFICULTY).unwrap();
let PoWPoW: PoW<PoW<String>> = PoW::prove_work(&PoW, DIFFICULTY).unwrap();
assert!(PoW.calculate(&phrase).unwrap() >= DIFFICULTY);
assert!(PoWPoW.calculate(&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 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();
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);
assert!(message.1.calculate(&message.0).unwrap() >= DIFFICULTY);
}
}