diff --git a/Cargo.toml b/Cargo.toml index 5d07ce1..0e0abea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,14 @@ [package] name = "pow" -version = "0.1.0" +version = "0.1.1" authors = ["Andrew Dirksen "] description = """ -Tag arbitrary data types with proof of work based on sha256. +Generate or verify sha256 based proofs of work over arbitrary typed data. """ edition = "2018" +documentation = "https://docs.rs/pow" +tags = ["proof of work", "pw set", "powtag", "tag"] +readme = "readme.md" license = "MIT OR Apache-2.0" [dependencies] diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..3657cfc --- /dev/null +++ b/readme.md @@ -0,0 +1,5 @@ +# Pow + +Typed proof of work tags. + +[docs](https://docs.rs/pow) diff --git a/src/lib.rs b/src/lib.rs index 57f1bbe..9f3050a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,10 @@ //! //! ``` //! use pow::Pow; -//! let difficulty = u128::max_value() / 2; // very easy mode +//! +//! // 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); @@ -22,14 +25,74 @@ //! # 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 +//! +//! // 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); //! ``` -//! Expected computional cost scales something like -//! O(u128::max_value() / (u128::max_value() - difficulty)) +//! # 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: +//! +//! ``` +//! # use serde::Serialize; +//! # use pow::Pow; +//! # use core::any::Any; +//! # const SALT: &'static str = "not the actual salt used"; +//! # fn 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(target: &T, pow_tag: &Pow) -> 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 +//! derterministic 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. +//! +//! ``` +//! 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. +//! +//! fn average(difficulty: u128) -> u128 { +//! let m = u128::max_value(); +//! if difficulty == m { +//! return m; +//! } +//! m / (m - difficulty) +//! } mod proof_of_work;