Skip to main content

spongefish_pow/
keccak.rs

1use super::PowStrategy;
2use crate::PoWSolution;
3use ::keccak::{Keccak, State1600};
4
5#[derive(Clone, Copy)]
6pub struct KeccakPoW {
7    challenge: [u64; 4],
8    threshold: u64,
9    state: [u64; 25],
10}
11
12impl PowStrategy for KeccakPoW {
13    #[allow(clippy::cast_sign_loss)]
14    fn new(challenge: [u8; 32], bits: f64) -> Self {
15        let threshold = (64.0 - bits).exp2().ceil() as u64;
16        Self {
17            challenge: bytemuck::cast(challenge),
18            threshold,
19            state: [0; 25],
20        }
21    }
22
23    fn solution(&self, nonce: u64) -> PoWSolution {
24        PoWSolution {
25            challenge: bytemuck::cast(self.challenge),
26            nonce,
27        }
28    }
29
30    fn check(&mut self, nonce: u64) -> bool {
31        self.state[..4].copy_from_slice(&self.challenge);
32        self.state[4] = nonce;
33        for s in self.state.iter_mut().skip(5) {
34            *s = 0;
35        }
36        f1600(&mut self.state);
37        self.state[0] < self.threshold
38    }
39}
40
41fn f1600(state: &mut State1600) {
42    Keccak::new().with_f1600(|f1600| f1600(state));
43}
44
45#[test]
46fn test_pow_keccak() {
47    use crate::{convenience::*, PoWGrinder};
48
49    const BITS: f64 = 10.0;
50
51    // Test with a fixed challenge
52    let challenge = [42u8; 32];
53
54    // Generate a proof-of-work solution
55    let _solution = grind_pow::<KeccakPoW>(challenge, BITS).expect("Should find a valid solution");
56
57    // We can't extract the nonce directly from the solution (it's one-way),
58    // but we can verify by re-grinding and checking we get a valid solution
59    let mut grounder = PoWGrinder::<KeccakPoW>::new(challenge, BITS);
60    let _solution2 = grounder.grind().expect("Should find a valid solution");
61
62    // Both solutions should be valid (though they contain different nonces)
63    // We verify by checking that grinding succeeds
64    assert!(grind_pow::<KeccakPoW>(challenge, BITS).is_some());
65}