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] [package]
name = "pow" name = "PoW-SHA256"
version = "0.1.5" version = "0.1.0"
authors = ["Andrew Dirksen <andrew@dirksen.com>"] authors = ["Robert Kornacki <robk@syre.io>"]
description = """ description = """
Generate or verify sha256 based proofs of work over arbitrary typed data. SHA256 PoW on any serializable datatype.
""" """
edition = "2018" edition = "2018"
documentation = "https://docs.rs/pow" # documentation = "https://docs.rs/PoW"
keywords = ["pwset", "powtag", "tag"] keywords = ["PoW", "sha256", "proof-of-work"]
readme = "readme.md" readme = "readme.md"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
repository = "https://github.com/bddap/pow" repository = "https://github.com/robkorn/pow"
categories = [] # pr me! categories = [] # pr me!
[dependencies] [dependencies]
sha2 = "0.8.0" sha2 = "0.8.0"
serde = { version = "1", features = ["derive"] } 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 "Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition, 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 direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity. 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. 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. Prove we did work targeting a phrase.
```rust ```rust
use pow::Pow; use PoW::PoW;
// very easy mode // very easy mode
let difficulty = u128::max_value() - u128::max_value() / 2; let difficulty = u128::max_value() - u128::max_value() / 2;
let phrase = b"Phrase to tag.".to_vec(); 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); 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 difficulty = u128::max_value() - u128::max_value() / 100_000;
let now: u64 = get_unix_time_seconds(); 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); assert!(pw.score(&now).unwrap() >= difficulty);
``` ```
@ -36,24 +36,24 @@ Define a blockchain block.
struct Block<T> { struct Block<T> {
prev: [u8; 32], // hash of last block prev: [u8; 32], // hash of last block
payload: T, // generic data payload: T, // generic data
proof_of_work: Pow<([u8; 32], T)>, proof_of_work: PoW<([u8; 32], T)>,
} }
``` ```
# Score scheme # Score scheme
To score a proof of work for a given (target, Pow) pair: To score a proof of work for a given (target, PoW) pair:
Sha256 is calculated over the concatenation SALT + target + Pow. Sha256 is calculated over the concatenation SALT + target + PoW.
The first 16 bytes of the hash are interpreted as a 128 bit unsigned integer. The first 16 bytes of the hash are interpreted as a 128 bit unsigned integer.
That integer is the score. 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. of work blockchains.
In other words: In other words:
```rust ```rust
fn score<T: Serialize>(target: &T, pow_tag: &Pow<T>) -> u128 { fn score<T: Serialize>(target: &T, PoW_tag: &PoW<T>) -> u128 {
let bytes = serialize(&SALT) + serialize(target) + serialize(pow_tag); let bytes = serialize(&SALT) + serialize(target) + serialize(PoW_tag);
let hash = sha256(&bytes); let hash = sha256(&bytes);
deserialize(&hash[..16]) deserialize(&hash[..16])
} }
@ -66,7 +66,7 @@ deterministic serialization. All values are serialized using network byte order.
# Threshold scheme # 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. # Choosing a difficulty setting.

View file

@ -1,112 +1,112 @@
//! Sha256 based proof of work over a typed piece of data. // 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. // Any type that implementes serde::Deserialize can be tagged with a proof of work.
//!
//! # Examples // # Examples
//!
//! Prove we did work targeting a phrase. // Prove we did work targeting a phrase.
//!
//! ```rust // ```rust
//! use pow::Pow; // use PoW::PoW;
//!
//! // very easy mode // // very easy mode
//! let difficulty = u128::max_value() - u128::max_value() / 2; // let difficulty = u128::max_value() - u128::max_value() / 2;
//!
//! let phrase = b"Phrase to tag.".to_vec(); // 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); // assert!(pw.calculate(&phrase).unwrap() >= difficulty);
//! ``` // ```
//!
//! Prove more difficult work. This time targeting a time. // Prove more difficult work. This time targeting a time.
//!
//! ```rust // ```rust
//! # fn get_unix_time_seconds() -> u64 { // # fn get_unix_time_seconds() -> u64 {
//! # use std::time::{Duration, SystemTime}; // # use std::time::{Duration, SystemTime};
//! # SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs() // # SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs()
//! # } // # }
//! # use pow::Pow; // # use PoW::PoW;
//!
//! // more diffcult, takes around 100_000 hashes to generate proof // // more diffcult, takes around 100_000 hashes to generate proof
//! let difficulty = u128::max_value() - u128::max_value() / 100_000; // let difficulty = u128::max_value() - u128::max_value() / 100_000;
//!
//! let now: u64 = get_unix_time_seconds(); // 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); // assert!(pw.calculate(&now).unwrap() >= difficulty);
//! ``` // ```
//!
//! Define a blockchain block. // Define a blockchain block.
//!
//! ```rust // ```rust
//! # use pow::Pow; // # use PoW::PoW;
//! struct Block<T> { // struct Block<T> {
//! prev: [u8; 32], // hash of last block // prev: [u8; 32], // hash of last block
//! payload: T, // generic data // payload: T, // generic data
//! proof_of_work: Pow<([u8; 32], T)>, // proof_of_work: PoW<([u8; 32], T)>,
//! } // }
//! ``` // ```
//!
//! # Score scheme // # Score scheme
//!
//! To score a proof of work for a given (target, Pow) pair: // To score a proof of work for a given (target, PoW) pair:
//! Sha256 is calculated over the concatenation SALT + target + Pow. // Sha256 is calculated over the concatenation SALT + target + PoW.
//! The first 16 bytes of the hash are interpreted as a 128 bit unsigned integer. // The first 16 bytes of the hash are interpreted as a 128 bit unsigned integer.
//! That integer is the score. // 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. // of work blockchains.
//!
//! In other words: // In other words:
//!
//! ```rust // ```rust
//! # use serde::Serialize; // # use serde::Serialize;
//! # use pow::Pow; // # use PoW::PoW;
//! # use core::any::Any; // # use core::any::Any;
//! # const SALT: &'static str = "not the actual salt used"; // # const SALT: &'static str = "not the actual salt used";
//! # fn serialize<T: Serialize>(_: &T) -> u8 { 0 } // not the actual serialize function // # fn serialize<T: Serialize>(_: &T) -> u8 { 0 } // not the actual serialize function
//! # fn deserialize(_: &[u8]) -> u128 { 0 } // not the actual deserialize function // # fn deserialize(_: &[u8]) -> u128 { 0 } // not the actual deserialize function
//! # fn sha256(_: &u8) -> [u8; 32] { [0; 32] } // not the actual sha256 function // # fn sha256(_: &u8) -> [u8; 32] { [0; 32] } // not the actual sha256 function
//! fn score<T: Serialize>(target: &T, pow_tag: &Pow<T>) -> u128 { // fn score<T: Serialize>(target: &T, PoW_tag: &PoW<T>) -> u128 {
//! let bytes = serialize(&SALT) + serialize(target) + serialize(pow_tag); // let bytes = serialize(&SALT) + serialize(target) + serialize(PoW_tag);
//! let hash = sha256(&bytes); // let hash = sha256(&bytes);
//! deserialize(&hash[..16]) // deserialize(&hash[..16])
//! } // }
//! ``` // ```
//!
//! # Serialization encoding. // # Serialization encoding.
//!
//! It shouldn't matter to users of this library, but the bincode crate is used for cheap // 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. // deterministic serialization. All values are serialized using network byte order.
//!
//! # Threshold scheme // # 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. // # Choosing a difficulty setting.
//!
//! Difficulty settings are usually best adjusted dynamically a la bitcoin. // Difficulty settings are usually best adjusted dynamically a la bitcoin.
//!
//! To manually select a difficulty, choose the average number of hashes required. // To manually select a difficulty, choose the average number of hashes required.
//!
//! ```rust // ```rust
//! fn difficulty(average: u128) -> u128 { // fn difficulty(average: u128) -> u128 {
//! debug_assert_ne!(average, 0, "It is impossible to prove work in zero attempts."); // debug_assert_ne!(average, 0, "It is impossible to prove work in zero attempts.");
//! let m = u128::max_value(); // let m = u128::max_value();
//! m - m / average // m - m / average
//! } // }
//! ``` // ```
//!
//! Conversely, to calculate probable number of hashes required to satisfy a given minimum // Conversely, to calculate probable number of hashes required to satisfy a given minimum
//! difficulty. // difficulty.
//!
//! ```rust // ```rust
//! fn average(difficulty: u128) -> u128 { // fn average(difficulty: u128) -> u128 {
//! let m = u128::max_value(); // let m = u128::max_value();
//! if difficulty == m { // if difficulty == m {
//! return m; // return m;
//! } // }
//! m / (m - difficulty) // m / (m - difficulty)
//! } // }
//! ``` // ```
mod proof_of_work; 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"; 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)] #[derive(Serialize, Deserialize, PartialEq, Clone, Copy, Debug)]
pub struct Pow<T> { pub struct PoW<T> {
nonce: u128, nonce: u128,
result: u128, result: u128,
_spook: PhantomData<T>, _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. // implementaion that performs sha256 lazily.
// `impl io::Write for sha2::Sha256 { ... }` // `impl io::Write for sha2::Sha256 { ... }`
impl<T: Serialize> Pow<T> { impl<T: Serialize> PoW<T> {
/// Prove work over 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 /// 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.
/// ///
/// Returns bincode::Error if serialization fails. /// 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() bincode_cfg()
.serialize(t) .serialize(t)
.map(|v| Self::prove_work_serialized(&v, difficulty)) .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. /// 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 /// 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: u128) -> Pow<T> { pub fn prove_work_serialized(prefix: &[u8], difficulty: u128) -> PoW<T> {
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;
let mut result = 0; let mut result = 0;
@ -42,23 +42,23 @@ impl<T: Serialize> Pow<T> {
n += 1; n += 1;
result = score(prefix_sha.clone(), n); result = score(prefix_sha.clone(), n);
} }
Pow { PoW {
nonce: n, nonce: n,
result: result, result: result,
_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) -> bincode::Result<u128> { pub fn calculate(&self, t: &T) -> bincode::Result<u128> {
bincode_cfg() bincode_cfg()
.serialize(t) .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. /// 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) score(Sha256::new().chain(SALT).chain(target), self.nonce)
} }
} }
@ -95,27 +95,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 = Pow::prove_work(&phrase, DIFFICULTY).unwrap(); let pw = PoW::prove_work(&phrase, DIFFICULTY).unwrap();
assert!(pw.score(&phrase).unwrap() >= DIFFICULTY); assert!(pw.calculate(&phrase).unwrap() >= DIFFICULTY);
} }
#[test] #[test]
fn double_pow() { fn double_pow() {
let phrase = "Corver bandar palladianism retroform.".to_owned(); let phrase = "Corver bandar palladianism retroform.".to_owned();
let pow = Pow::prove_work(&phrase, DIFFICULTY).unwrap(); let PoW = PoW::prove_work(&phrase, DIFFICULTY).unwrap();
let powpow: Pow<Pow<String>> = Pow::prove_work(&pow, DIFFICULTY).unwrap(); let PoWPoW: PoW<PoW<String>> = PoW::prove_work(&PoW, DIFFICULTY).unwrap();
assert!(pow.score(&phrase).unwrap() >= DIFFICULTY); assert!(PoW.calculate(&phrase).unwrap() >= DIFFICULTY);
assert!(powpow.score(&pow).unwrap() >= DIFFICULTY); assert!(PoWPoW.calculate(&PoW).unwrap() >= DIFFICULTY);
} }
#[test] #[test]
fn ser_de() { fn ser_de() {
let target: u8 = 1; let target: u8 = 1;
let pw = Pow::prove_work(&target, DIFFICULTY).unwrap(); let pw = PoW::prove_work(&target, DIFFICULTY).unwrap();
let message: (u8, Pow<u8>) = (target, pw); let message: (u8, PoW<u8>) = (target, pw);
let message_ser = bincode_cfg().serialize(&message).unwrap(); 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_eq!(recieved_message, message);
assert!(message.1.score(&message.0).unwrap() >= DIFFICULTY); assert!(message.1.calculate(&message.0).unwrap() >= DIFFICULTY);
} }
} }