Skip to content

Commit 5537a3b

Browse files
committed
Add keccak
1 parent fb54c5b commit 5537a3b

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

src/plugins/pow/keccak.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use super::PowStrategy;
2+
3+
#[derive(Clone, Copy)]
4+
pub struct KeccakPoW {
5+
challenge: [u64; 4],
6+
threshold: u64,
7+
state: [u64; 25],
8+
}
9+
10+
impl PowStrategy for KeccakPoW {
11+
fn new(challenge: [u8; 32], bits: f64) -> Self {
12+
let threshold = (64.0 - bits).exp2().ceil() as u64;
13+
Self {
14+
challenge: bytemuck::cast(challenge),
15+
threshold,
16+
state: [0; 25],
17+
}
18+
}
19+
20+
/// This deliberately uses the high level interface to guarantee
21+
/// compatibility with standard Blake3.
22+
fn check(&mut self, nonce: u64) -> bool {
23+
self.state[..4].copy_from_slice(&self.challenge);
24+
self.state[4] = nonce;
25+
for s in self.state.iter_mut().skip(5) {
26+
*s = 0;
27+
}
28+
keccak::f1600(&mut self.state);
29+
self.state[0] < self.threshold
30+
}
31+
}

src/plugins/pow/mod.rs

+39-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod blake3;
2+
mod keccak;
23

34
use crate::{
45
Arthur, ByteChallenges, ByteIOPattern, ByteReader, ByteWriter, IOPattern, Merlin, ProofError,
@@ -60,7 +61,7 @@ where
6061
}
6162
}
6263

63-
pub trait PowStrategy {
64+
pub trait PowStrategy: Clone + Sync {
6465
/// Creates a new proof-of-work challenge.
6566
/// The `challenge` is a 32-byte array that represents the challenge.
6667
/// The `bits` is the binary logarithm of the expected amount of work.
@@ -71,5 +72,41 @@ pub trait PowStrategy {
7172
fn check(&mut self, nonce: u64) -> bool;
7273

7374
/// Finds the minimal `nonce` that satisfies the challenge.
74-
fn solve(&mut self) -> Option<u64>;
75+
#[cfg(not(feature = "parallel"))]
76+
fn solve(&mut self) -> Option<u64> {
77+
// TODO: Parallel default impl
78+
(0u64..).find_map(|nonce| if self.check(nonce) { Some(nonce) } else { None })
79+
}
80+
81+
#[cfg(feature = "parallel")]
82+
fn solve(&mut self) -> Option<u64> {
83+
// Split the work across all available threads.
84+
// Use atomics to find the unique deterministic lowest satisfying nonce.
85+
86+
use std::sync::atomic::{AtomicU64, Ordering};
87+
88+
use rayon::broadcast;
89+
let global_min = AtomicU64::new(u64::MAX);
90+
let _ = broadcast(|ctx| {
91+
let mut worker = self.clone();
92+
let nonces = (ctx.index() as u64..).step_by(ctx.num_threads());
93+
for nonce in nonces {
94+
// Use relaxed ordering to eventually get notified of another thread's solution.
95+
// (Propagation delay should be in the order of tens of nanoseconds.)
96+
if nonce >= global_min.load(Ordering::Relaxed) {
97+
break;
98+
}
99+
if worker.check(nonce) {
100+
// We found a solution, store it in the global_min.
101+
// Use fetch_min to solve race condition with simultaneous solutions.
102+
global_min.fetch_min(nonce, Ordering::SeqCst);
103+
break;
104+
}
105+
}
106+
});
107+
match global_min.load(Ordering::SeqCst) {
108+
u64::MAX => self.check(u64::MAX).then_some(u64::MAX),
109+
nonce => Some(nonce),
110+
}
111+
}
75112
}

0 commit comments

Comments
 (0)