1use ::keccak::{Keccak, State1600};
2
3use super::PowStrategy;
4use crate::PoWSolution;
5
6#[derive(Clone, Copy)]
7pub struct KeccakPoW {
8 challenge: [u64; 4],
9 threshold: u64,
10 state: [u64; 25],
11}
12
13impl PowStrategy for KeccakPoW {
14 #[allow(clippy::cast_sign_loss)]
19 fn new(challenge: [u8; 32], bits: f64) -> Self {
20 assert!((0.0..60.0).contains(&bits), "bits must be smaller than 60");
29
30 let threshold = (64.0 - bits).exp2().ceil() as u64;
31 Self {
32 challenge: bytemuck::cast(challenge),
33 threshold,
34 state: [0; 25],
35 }
36 }
37
38 fn solution(&self, nonce: u64) -> PoWSolution {
39 PoWSolution {
40 challenge: bytemuck::cast(self.challenge),
41 nonce,
42 }
43 }
44
45 fn check(&mut self, nonce: u64) -> bool {
46 self.state[..4].copy_from_slice(&self.challenge);
47 self.state[4] = nonce;
48 for s in self.state.iter_mut().skip(5) {
49 *s = 0;
50 }
51 f1600(&mut self.state);
52 self.state[0] < self.threshold
53 }
54}
55
56fn f1600(state: &mut State1600) {
57 Keccak::new().with_f1600(|f1600| f1600(state));
58}
59
60#[test]
61fn test_pow_keccak() {
62 use crate::{convenience::*, PoWGrinder};
63
64 const BITS: f64 = 10.0;
65
66 let challenge = [42u8; 32];
68
69 let solution = grind_pow::<KeccakPoW>(challenge, BITS).expect("Should find a valid solution");
71
72 let mut grinder = PoWGrinder::<KeccakPoW>::new(challenge, BITS);
75 let solution2 = grinder.grind().expect("Should find a valid solution");
76 assert_eq!(solution.nonce, solution2.nonce);
77
78 assert!(verify_pow::<KeccakPoW>(challenge, BITS, solution.nonce));
80}
81
82#[test]
85#[should_panic(expected = "bits must be smaller than 60")]
86fn test_keccak_rejects_negative_bits() {
87 let _ = <KeccakPoW as PowStrategy>::new([0u8; 32], -1.0);
88}
89
90#[test]
92#[should_panic(expected = "bits must be smaller than 60")]
93fn test_keccak_rejects_excessive_bits() {
94 let _ = <KeccakPoW as PowStrategy>::new([0u8; 32], 60.0);
95}
96
97#[test]
99fn test_keccak_valid_difficulty_round_trip() {
100 use crate::convenience::*;
101
102 let challenge = [7u8; 32];
103 for bits in [0.0, 1.0, 8.0, 12.0] {
104 let solution =
105 grind_pow::<KeccakPoW>(challenge, bits).expect("Should find a valid solution");
106 assert!(verify_pow::<KeccakPoW>(challenge, bits, solution.nonce));
107 assert_eq!(solution.challenge, challenge);
108 }
109}