diff --git a/Cargo.toml b/Cargo.toml index 0c20946..2c9b71c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,16 @@ [package] name = "pow_sha256" version = "0.1.0" -authors = ["Robert Kornacki , Aravinth Manivannan "] +authors = [ "Aravinth Manivannan ", "Robert Kornacki "] description = """ -SHA256 PoW on any serializable datatype. +SHA256 PoW on any serializable datatype used in mCaptcha """ edition = "2018" -keywords = ["PoW", "sha256", "proof-of-work"] -readme = "readme.md" +keywords = ["mCaptcha", "PoW", "sha256", "proof-of-work"] +readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/mcaptcha/pow_sha256" -categories = ["algorithms", "cryptography::cryptocurrencies"] +categories = ["captcha", "algorithms", "cryptography::cryptocurrencies"] [dependencies] diff --git a/README.md b/README.md index 94cb7a6..4d6ad5f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ - -
-

PoW_SHA256

+

PoW-SHA256

- PoW_SHA256 - SHA256 based Proof-of-Work + PoW-SHA256 - SHA256 based Proof-of-Work

[![Documentation](https://img.shields.io/badge/docs-master-blue)](https://mcaptcha.github.io/pow_sha256/pow_sha256/index.html) @@ -14,48 +12,51 @@
-> pow_sha256's copy of `pow_sha256` by -> [robkorn](https://github.com/robkorn/pow_sha256) +> pow_sha256's copy of `pow_sha256` by +> [robkorn](https://github.com/robkorn/pow_sha256) > which is a modified version of [`pow` library](https://github.com/bddap/pow). > All copyrights belong to the original authors. -Rust crate which generates SHA256 Proofs of Work on serializable datatypes. +Rust crate which generates SHA256 Proofs of Work on serializable datatypes. Whether for blockchain-related projects or Hashcash-like schemes, this crate can be used to prove work was done on a given serializable input. The input merely needs to implement `serde::Deserialize` to be used. This is a fork of the [`pow` library](https://github.com/bddap/pow) by -bddap with some new additions. Primary of these being: +[@robkorn](https://github.com/robkorn/pow_sha256)) with some new +additions. Primary of these being: -- PoW datatype now saves the calculation result to be used for checking - proof validity given input -- `is_valid_proof` method to do the above mentioned -- PoW datatype no longer saves `u128` values as these are unsupported by - popular serialization formats (CBOR, Msgpack, ...) -- `is_sufficient_difficulty` method to check difficulty with new changes +- PoW datatype now offers a constructor +- Salt is no longer hard coded into the library, users can provide + unique salts. Other small changes have also been included of various importance but mostly just stylistic/ease of use improvements. -# Examples +## Examples -Prove work was done, specifically targeting a phrase. +Prove work specifically targeting a phrase. ```rust -use pow_sha256::PoW; -// Very easy difficulty -let difficulty = u128::max_value() - u128::max_value() / 2; +use pow_sha256::{ConfigBuilder, PoW}; -let phrase = b"Phrase to be used.".to_vec(); -let pw = PoW::prove_work(&phrase, difficulty).unwrap(); +fn main() { + let config = ConfigBuilder::default() + .salt("myrandomsaltisnotlongenoug".into()) + .build() + .unwrap(); -// Asserting that the result is of sufficient difficulty -assert!(pw.is_sufficient_difficulty(difficulty)); + let phrase = "ironmansucks"; -// Asserting that the PoW was generated from the provided phrase -assert!(pw.is_valid_proof(&phrase)) + const DIFFICULTY: u128 = u128::MAX / 32; + + let work = config.prove_work(&phrase, DIFFICULTY).unwrap(); + assert!(config.calculate(&work, &phrase).unwrap() >= DIFFICULTY); + assert!(config.is_valid_proof(&work, &phrase)); + assert!(config.is_sufficient_difficulty(&work, DIFFICULTY)); +} ``` Prove more difficult work. This time targeting a time. @@ -63,31 +64,44 @@ Prove more difficult work. This time targeting a time. ```rust // Greater difficulty this time around. Takes around 100,000 hashes // to find a nonce of the correct difficulty. -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.is_sufficient_difficulty(difficulty)); -assert!(pw.is_valid_proof(&phrase)) +use pow_sha256::{ConfigBuilder, PoW}; + +fn main() { + let config = ConfigBuilder::default() + .salt("myrandomsaltisnotlongenoug".into()) + .build() + .unwrap(); + + let phrase = "ironmansucks"; + + const DIFFICULTY: u128 = u128::max_value() - u128::max_value() / 100_000; + + let work = config.prove_work(&phrase, DIFFICULTY).unwrap(); + + assert!(config.calculate(&work, &phrase).unwrap() >= DIFFICULTY); + assert!(config.is_valid_proof(&work, &phrase)); + assert!(config.is_sufficient_difficulty(&work, DIFFICULTY)); +} + ``` +## Hashing Scheme -# Hashing Scheme - -A randomly generated constant, `SALT`, is used as prefix to prevent PoW -reuse from other systems such as proof of work blockchains. +`SALT` is used as prefix to prevent PoW reuse from other systems such as +proof of work blockchains. SHA256 is calculated over the concatenation of the: + - SALT -- Serialized Input `T` +- Serialized Input `T` - Nonce The first 16 bytes of the resulting hash are interpreted as a 128 bit unsigned integer and saved as the final result. - -# Choosing a difficulty setting. +## Choosing a difficulty setting. Depending on your use case, difficulty settings often are best set dynamically a la bitcoin. @@ -104,14 +118,15 @@ fn get_difficulty(average: u128) -> u128 { } ``` -Conversely we can use the same equation to calculate the probable number of hashes required to satisfy a given difficulty: +Conversely we can use the same equation to calculate the probable number +of hashes required to satisfy a given difficulty: ```rust fn est_average(difficulty: u128) -> u128 { let m = u128::max_value(); if difficulty == m { return m; - } + } m / (m - difficulty) } ``` diff --git a/examples/simple.rs b/examples/simple.rs new file mode 100644 index 0000000..a90fce4 --- /dev/null +++ b/examples/simple.rs @@ -0,0 +1,21 @@ +/* The easiest way to use this crate is with the default configuration. + * See `Default` implementation for the default configuration. + */ + +use pow_sha256::{ConfigBuilder, PoW}; + +fn main() { + let config = ConfigBuilder::default() + .salt("myrandomsaltisnotlongenoug".into()) + .build() + .unwrap(); + + let phrase = "ironmansucks"; + + const DIFFICULTY: u128 = u128::MAX / 32; + + let work = config.prove_work(&phrase, DIFFICULTY).unwrap(); + assert!(config.calculate(&work, &phrase).unwrap() >= DIFFICULTY); + assert!(config.is_valid_proof(&work, &phrase)); + assert!(config.is_sufficient_difficulty(&work, DIFFICULTY)); +} diff --git a/src/lib.rs b/src/lib.rs index 75e5561..7620f3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,26 @@ +//! MCaptch's SHA256 based Proof of Work library +//! +//! # Example: +//! ```rust +//! use pow_sha256::{ConfigBuilder, PoW}; +//! +//! fn main() { +//! let config = ConfigBuilder::default() +//! .salt("myrandomsaltisnotlongenoug".into()) +//! .build() +//! .unwrap(); +//! +//! let phrase = "ironmansucks"; +//! +//! const DIFFICULTY: u128 = u128::MAX / 32; +//! +//! let work = config.prove_work(&phrase, DIFFICULTY).unwrap(); +//! assert!(config.calculate(&work, &phrase).unwrap() >= DIFFICULTY); +//! assert!(config.is_valid_proof(&work, &phrase)); +//! assert!(config.is_sufficient_difficulty(&work, DIFFICULTY)); +//! } +//! ``` + use std::marker::PhantomData; use derive_builder::Builder; @@ -13,7 +36,10 @@ pub struct PoW { _spook: PhantomData, } -/// Proof of Work over concrete type T. T can be any type that implements serde::Serialize. +/// Configuration for generting proof of work +/// Please choose a long, unique value for salt +/// Resistance to dictionary/rainbow attacks depend on uniqueness +/// of the salt #[derive(Serialize, Deserialize, Builder, PartialEq, Clone, Debug)] pub struct Config { pub salt: String, @@ -22,8 +48,8 @@ pub struct Config { impl Config { /// 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. + /// 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(&self, t: &T, difficulty: u128) -> bincode::Result> where @@ -35,8 +61,8 @@ impl Config { /// 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. + /// 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(&self, prefix: &[u8], difficulty: u128) -> PoW where T: Serialize,