variable salt and init pow
This commit is contained in:
parent
01755af3d4
commit
2a23e064e1
3 changed files with 161 additions and 43 deletions
81
Cargo.lock
generated
81
Cargo.lock
generated
|
@ -39,6 +39,66 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"derive_builder_core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
|
@ -48,6 +108,12 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.4"
|
||||
|
@ -58,6 +124,12 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
|
@ -66,9 +138,10 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
|||
|
||||
[[package]]
|
||||
name = "pow_sha256"
|
||||
version = "0.2.1"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"derive_builder",
|
||||
"serde",
|
||||
"sha2",
|
||||
]
|
||||
|
@ -124,6 +197,12 @@ dependencies = [
|
|||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.61"
|
||||
|
|
|
@ -17,3 +17,4 @@ categories = ["algorithms", "cryptography::cryptocurrencies"]
|
|||
sha2 = "0.9"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
bincode = "1.3"
|
||||
derive_builder = "0.9"
|
||||
|
|
122
src/lib.rs
122
src/lib.rs
|
@ -1,27 +1,35 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use derive_builder::Builder;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
const SALT: &str = "79ziepia7vhjgviiwjhnend3ofjqocsi2winc4ptqhmkvcajihywxcizewvckg9h6gs4j83v9";
|
||||
|
||||
/// Proof of Work over concrete type T. T can be any type that implements serde::Serialize.
|
||||
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
|
||||
#[derive(Serialize, Builder, Deserialize, PartialEq, Clone, Debug)]
|
||||
pub struct PoW<T> {
|
||||
pub nonce: u64,
|
||||
pub result: String,
|
||||
#[builder(default = "PhantomData", setter(skip))]
|
||||
_spook: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Serialize> PoW<T> {
|
||||
/// Proof of Work over concrete type T. T can be any type that implements serde::Serialize.
|
||||
#[derive(Serialize, Deserialize, Builder, PartialEq, Clone, Debug)]
|
||||
pub struct Config {
|
||||
pub salt: String,
|
||||
}
|
||||
|
||||
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.
|
||||
///
|
||||
/// Returns bincode::Error if serialization fails.
|
||||
pub fn prove_work(t: &T, difficulty: u128) -> bincode::Result<PoW<T>> {
|
||||
bincode::serialize(t).map(|v| Self::prove_work_serialized(&v, difficulty))
|
||||
pub fn prove_work<T>(&self, t: &T, difficulty: u128) -> bincode::Result<PoW<T>>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
bincode::serialize(t).map(|v| self.prove_work_serialized(&v, difficulty))
|
||||
}
|
||||
|
||||
/// Create Proof of Work on an already serialized item of type T.
|
||||
|
@ -29,8 +37,11 @@ impl<T: Serialize> PoW<T> {
|
|||
///
|
||||
/// 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> {
|
||||
let prefix_sha = Sha256::new().chain(SALT).chain(prefix);
|
||||
pub fn prove_work_serialized<T>(&self, prefix: &[u8], difficulty: u128) -> PoW<T>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let prefix_sha = Sha256::new().chain(&self.salt).chain(prefix);
|
||||
let mut n = 0;
|
||||
let mut result = 0;
|
||||
while result < difficulty {
|
||||
|
@ -45,21 +56,30 @@ impl<T: Serialize> PoW<T> {
|
|||
}
|
||||
|
||||
/// Calculate the PoW score with the provided input T.
|
||||
pub fn calculate(&self, t: &T) -> bincode::Result<u128> {
|
||||
bincode::serialize(t).map(|v| self.calculate_serialized(&v))
|
||||
pub fn calculate<T>(&self, pow: &PoW<T>, t: &T) -> bincode::Result<u128>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
bincode::serialize(t).map(|v| self.calculate_serialized(pow, &v))
|
||||
}
|
||||
|
||||
/// Calculate the PoW score of an already serialized T and self.
|
||||
/// The input is assumed to be serialized using network byte order.
|
||||
pub fn calculate_serialized(&self, target: &[u8]) -> u128 {
|
||||
score(Sha256::new().chain(SALT).chain(target), self.nonce)
|
||||
pub fn calculate_serialized<T>(&self, pow: &PoW<T>, target: &[u8]) -> u128
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
score(Sha256::new().chain(&self.salt).chain(target), pow.nonce)
|
||||
}
|
||||
|
||||
/// Verifies that the PoW is indeed generated out of the phrase provided.
|
||||
pub fn is_valid_proof(&self, t: &T) -> bool {
|
||||
match self.calculate(t) {
|
||||
pub fn is_valid_proof<T>(&self, pow: &PoW<T>, t: &T) -> bool
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
match self.calculate(pow, t) {
|
||||
Ok(res) => {
|
||||
return if self.result == res.to_string() {
|
||||
return if pow.result == res.to_string() {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -70,8 +90,11 @@ impl<T: Serialize> PoW<T> {
|
|||
}
|
||||
|
||||
/// Checks if the PoW result is of sufficient difficulty
|
||||
pub fn is_sufficient_difficulty(&self, target_diff: u128) -> bool {
|
||||
match self.result.parse::<u128>() {
|
||||
pub fn is_sufficient_difficulty<T>(&self, pow: &PoW<T>, target_diff: u128) -> bool
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
match pow.result.parse::<u128>() {
|
||||
Ok(res) => return res >= target_diff,
|
||||
Err(_) => return false,
|
||||
}
|
||||
|
@ -94,59 +117,74 @@ fn first_bytes_as_u128(inp: &[u8]) -> u128 {
|
|||
bincode::deserialize(&inp).unwrap()
|
||||
}
|
||||
|
||||
//fn bincode_cfg() -> bincode::Config {
|
||||
// let mut cfg = bincode::config();
|
||||
// cfg.big_endian();
|
||||
// cfg
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
const DIFFICULTY: u128 = 0xff000000000000000000000000000000;
|
||||
|
||||
fn get_config() -> Config {
|
||||
ConfigBuilder::default()
|
||||
.salt(
|
||||
"79ziepia7vhjgviiwjhnend3ofjqocsi2winc4ptqhmkvcajihywxcizewvckg9h6gs4j83v9".into(),
|
||||
)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn base_functionality() {
|
||||
// Let's prove we did work targeting a phrase.
|
||||
let phrase = b"Ex nihilo nihil fit.".to_vec();
|
||||
let pw = PoW::prove_work(&phrase, DIFFICULTY).unwrap();
|
||||
assert!(pw.calculate(&phrase).unwrap() >= DIFFICULTY);
|
||||
assert!(pw.is_valid_proof(&phrase));
|
||||
assert!(pw.is_sufficient_difficulty(DIFFICULTY));
|
||||
let config = get_config();
|
||||
let pw = config.prove_work(&phrase, DIFFICULTY).unwrap();
|
||||
assert!(config.calculate(&pw, &phrase).unwrap() >= DIFFICULTY);
|
||||
assert!(config.is_valid_proof(&pw, &phrase));
|
||||
assert!(config.is_sufficient_difficulty(&pw, DIFFICULTY));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_pow() {
|
||||
let phrase = "Ex nihilo nihil fit.".to_owned();
|
||||
let pw = PoW::prove_work(&phrase, DIFFICULTY).unwrap();
|
||||
let pwpw: PoW<PoW<String>> = PoW::prove_work(&pw, DIFFICULTY).unwrap();
|
||||
assert!(pw.calculate(&phrase).unwrap() >= DIFFICULTY);
|
||||
assert!(pwpw.calculate(&pw).unwrap() >= DIFFICULTY);
|
||||
assert!(pw.is_sufficient_difficulty(DIFFICULTY));
|
||||
assert!(pwpw.is_sufficient_difficulty(DIFFICULTY));
|
||||
assert!(pw.is_valid_proof(&phrase));
|
||||
assert!(pwpw.is_valid_proof(&pw));
|
||||
let config = get_config();
|
||||
|
||||
let pw = config.prove_work(&phrase, DIFFICULTY).unwrap();
|
||||
let pwpw = config.prove_work(&pw, DIFFICULTY).unwrap();
|
||||
|
||||
assert!(config.calculate(&pw, &phrase).unwrap() >= DIFFICULTY);
|
||||
assert!(config.is_valid_proof(&pw, &phrase));
|
||||
assert!(config.is_sufficient_difficulty(&pw, DIFFICULTY));
|
||||
|
||||
assert!(config.calculate(&pwpw, &pw).unwrap() >= DIFFICULTY);
|
||||
assert!(config.is_valid_proof(&pwpw, &pw));
|
||||
assert!(config.is_sufficient_difficulty(&pwpw, DIFFICULTY));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_not_valid_proof() {
|
||||
let phrase = "Ex nihilo nihil fit.".to_owned();
|
||||
let phrase2 = "Omne quod movetur ab alio movetur.".to_owned();
|
||||
let pw = PoW::prove_work(&phrase, DIFFICULTY).unwrap();
|
||||
let pw2 = PoW::prove_work(&phrase2, DIFFICULTY).unwrap();
|
||||
assert!(!pw.is_valid_proof(&phrase2));
|
||||
assert!(!pw2.is_valid_proof(&phrase));
|
||||
|
||||
let config = get_config();
|
||||
let pw = config.prove_work(&phrase, DIFFICULTY).unwrap();
|
||||
|
||||
let pw2 = config.prove_work(&phrase2, DIFFICULTY).unwrap();
|
||||
|
||||
assert!(!config.is_valid_proof(&pw, &phrase2));
|
||||
assert!(!config.is_valid_proof(&pw2, &phrase));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialization_test() {
|
||||
let target: u8 = 1;
|
||||
let pw = PoW::prove_work(&target, DIFFICULTY).unwrap();
|
||||
let config = get_config();
|
||||
let pw = config.prove_work(&target, DIFFICULTY).unwrap();
|
||||
|
||||
let message: (u8, PoW<u8>) = (target, pw);
|
||||
let message_ser = bincode::serialize(&message).unwrap();
|
||||
let recieved_message: (u8, PoW<u8>) = bincode::deserialize(&message_ser).unwrap();
|
||||
assert_eq!(recieved_message, message);
|
||||
assert!(message.1.is_sufficient_difficulty(DIFFICULTY));
|
||||
assert!(message.1.is_valid_proof(&target));
|
||||
assert!(config.is_sufficient_difficulty(&message.1, DIFFICULTY));
|
||||
assert!(config.is_valid_proof(&message.1, &target));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue