From 0e96f03166a1610b838314cd8ddfb504d0609f2e Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Tue, 4 Jul 2023 19:03:32 +0530 Subject: [PATCH 1/2] feat: implement stepped proof generation to report progess ref: https://git.batsense.net/mCaptcha/2023-NLnet/issues/8 --- src/lib.rs | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index cf8b208..bdd356c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,7 +50,74 @@ pub struct Config { pub salt: String, } +pub enum SteppedSolve { + Intermediate(u128, u64, Sha256, u128), + Work(PoW), +} + impl Config { + pub fn stepped_prove_work( + &self, + t: &T, + difficulty: u32, + step: usize, + inter: Option>, + ) -> bincode::Result> + where + T: Serialize, + { + bincode::serialize(t) + .map(|v| self.stepped_prove_work_serialized(&v, difficulty, step, inter)) + } + + /// 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. + pub fn stepped_prove_work_serialized( + &self, + prefix: &[u8], + difficulty: u32, + step: usize, + inter: Option>, + ) -> SteppedSolve + where + T: Serialize, + { + let (mut result, mut n, prefix_sha, difficulty) = match inter { + Some(SteppedSolve::Intermediate(result, nonce, prefix, difficulty)) => { + (result, nonce, prefix, difficulty) + } + _ => { + let prefix_sha = Sha256::new().chain(&self.salt).chain(prefix); + let n = 0; + let result = 0; + let difficulty = get_difficulty(difficulty); + (result, n, prefix_sha, difficulty) + } + }; + let mut count = 0; + // let prefix_sha = Sha256::new().chain(&self.salt).chain(prefix); + // let mut n = 0; + // let mut result = 0; + // let difficulty = get_difficulty(difficulty); + while result < difficulty { + if count > step { + return SteppedSolve::Intermediate(result, n, prefix_sha, difficulty); + } else { + count += 1; + } + n += 1; + result = dev::score(prefix_sha.clone(), n); + } + SteppedSolve::Work(PoW { + nonce: n, + result: result.to_string(), + _spook: PhantomData, + }) + } + /// Create Proof of Work over item of type T. /// /// Make sure difficulty is not too high. A 64 bit difficulty, @@ -262,4 +329,30 @@ mod test { assert!(config.is_sufficient_difficulty(&message.1, DIFFICULTY)); assert!(config.is_valid_proof(&message.1, &target)); } + + #[test] + fn stepped_solve() { + let phrase = "Ex nihilo nihil fit.".to_owned(); + let config = get_config(); + + let mut inter = None; + loop { + match config.stepped_prove_work(&phrase, 50000, 1000, inter) { + Ok(SteppedSolve::Intermediate(result, nonce, prefix, difficulty)) => { + println!("Current nonce {nonce}"); + inter = Some(SteppedSolve::Intermediate( + result, nonce, prefix, difficulty, + )); + continue; + } + + Ok(SteppedSolve::Work(w)) => { + assert!(config.is_valid_proof(&w, &phrase)); + assert!(config.is_sufficient_difficulty(&w, DIFFICULTY)); + break; + } + Err(e) => panic!("{}", e), + }; + } + } } From 12d75333394b00bc4eb609dee7f0f2af7c0521ba Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Tue, 4 Jul 2023 20:04:52 +0530 Subject: [PATCH 2/2] feat: feature gate incremental function --- .github/workflows/linux.yml | 4 +-- Cargo.toml | 4 +++ Makefile | 2 +- src/lib.rs | 55 +++++++++++++++++++++++++------------ 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index d8f9d53..ff54f5a 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -42,7 +42,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: check - args: --all --bins --examples --tests + args: --all --bins --examples --tests --all-features - name: tests uses: actions-rs/cargo@v1 @@ -56,7 +56,7 @@ jobs: uses: actions-rs/tarpaulin@v0.1 with: version: '0.15.0' - args: '-t 1200' + args: '-t 1200 --all-features' - name: Upload to Codecov if: matrix.version == 'stable' && (github.ref == 'refs/heads/master' || github.event_name == 'pull_request') diff --git a/Cargo.toml b/Cargo.toml index a470965..4ed7d2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,7 @@ serde = { version = "1.0", features = ["derive"] } bincode = "1.3" derive_builder = "0.12" num = { version = "0.4.0", default-features = false, features = ["serde", "num-bigint"]} + +[features] +default = [] +incremental = [] diff --git a/Makefile b/Makefile index 78f6ed1..3ac8e7c 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: AGPL-3.0-or-later define test_core - cargo test --no-fail-fast + cargo test --no-fail-fast --all-features endef default: ## Build library in debug mode diff --git a/src/lib.rs b/src/lib.rs index bdd356c..5df5dd6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,19 +50,32 @@ pub struct Config { pub salt: String, } -pub enum SteppedSolve { +/// Return value of incremental prooving. When proof is ready, IncrementalSolve::Work is returned +/// and when proof is not ready, IncrementalSolve::Intermediate is returned +#[cfg(feature = "incremental")] +pub enum IncrementalSolve { + /// Intermediate result Intermediate(u128, u64, Sha256, u128), + /// Final result Work(PoW), } impl Config { + /// + /// step is used to control the number of cycles after which the function should exit, even + /// when the proof isn't ready + /// inter is used to keep track of state and complete proof generation. Set inter to None + /// during first cyle and pass the returned value of the previous cycle to continue proof + /// generation + + #[cfg(feature = "incremental")] pub fn stepped_prove_work( &self, t: &T, difficulty: u32, step: usize, - inter: Option>, - ) -> bincode::Result> + inter: Option>, + ) -> bincode::Result> where T: Serialize, { @@ -70,23 +83,34 @@ impl Config { .map(|v| self.stepped_prove_work_serialized(&v, difficulty, step, inter)) } - /// Create Proof of Work on an already serialized item of type T. + #[cfg(feature = "incremental")] + /// 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. /// 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. - pub fn stepped_prove_work_serialized( + /// step is used to control the number of cycles after which the function should exit, even + /// when the proof isn't ready + /// inter is used to keep track of state and complete proof generation. Set inter to None + /// during first cyle and pass the returned value of the previous cycle to continue proof + /// generation + /// Returns bincode::Error if serialization fails. + /// Create Proof of Work on an already serialized item of type T. + fn stepped_prove_work_serialized( &self, prefix: &[u8], difficulty: u32, step: usize, - inter: Option>, - ) -> SteppedSolve + inter: Option>, + ) -> IncrementalSolve where T: Serialize, { let (mut result, mut n, prefix_sha, difficulty) = match inter { - Some(SteppedSolve::Intermediate(result, nonce, prefix, difficulty)) => { + Some(IncrementalSolve::Intermediate(result, nonce, prefix, difficulty)) => { (result, nonce, prefix, difficulty) } _ => { @@ -98,20 +122,16 @@ impl Config { } }; let mut count = 0; - // let prefix_sha = Sha256::new().chain(&self.salt).chain(prefix); - // let mut n = 0; - // let mut result = 0; - // let difficulty = get_difficulty(difficulty); while result < difficulty { if count > step { - return SteppedSolve::Intermediate(result, n, prefix_sha, difficulty); + return IncrementalSolve::Intermediate(result, n, prefix_sha, difficulty); } else { count += 1; } n += 1; result = dev::score(prefix_sha.clone(), n); } - SteppedSolve::Work(PoW { + IncrementalSolve::Work(PoW { nonce: n, result: result.to_string(), _spook: PhantomData, @@ -331,6 +351,7 @@ mod test { } #[test] + #[cfg(feature = "incremental")] fn stepped_solve() { let phrase = "Ex nihilo nihil fit.".to_owned(); let config = get_config(); @@ -338,15 +359,15 @@ mod test { let mut inter = None; loop { match config.stepped_prove_work(&phrase, 50000, 1000, inter) { - Ok(SteppedSolve::Intermediate(result, nonce, prefix, difficulty)) => { + Ok(IncrementalSolve::Intermediate(result, nonce, prefix, difficulty)) => { println!("Current nonce {nonce}"); - inter = Some(SteppedSolve::Intermediate( + inter = Some(IncrementalSolve::Intermediate( result, nonce, prefix, difficulty, )); continue; } - Ok(SteppedSolve::Work(w)) => { + Ok(IncrementalSolve::Work(w)) => { assert!(config.is_valid_proof(&w, &phrase)); assert!(config.is_sufficient_difficulty(&w, DIFFICULTY)); break;