diff --git a/Cargo.toml b/Cargo.toml index d242434..90e7eb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,17 @@ [package] name = "pow" -version = "0.1.2" +version = "0.1.3" authors = ["Andrew Dirksen "] description = """ 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"] +keywords = ["pwset", "powtag", "tag"] readme = "readme.md" license = "MIT OR Apache-2.0" +repository = "https://github.com/bddap/pow" +categories = [] # pr me! [dependencies] sha2 = "0.8.0" diff --git a/readme.md b/readme.md index 3657cfc..84ae47b 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,86 @@ # Pow -Typed proof of work tags. +Sha256 based proof of work over a typed piece of data. -[docs](https://docs.rs/pow) +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; + +// 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. + +``` +// 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); +``` + +# 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: + +``` +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 +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. + +``` +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) +} +``` diff --git a/src/lib.rs b/src/lib.rs index b697e56..5896291 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,7 @@ //! # 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. +//! deterministic serialization. All values are serialized using network byte order. //! //! # Threshold scheme //!