1pub mod blake3;
2pub mod keccak;
3
4use spongefish::{
5 ByteDomainSeparator, BytesToUnitDeserialize, BytesToUnitSerialize, DuplexSpongeInterface,
6 ProofError, ProofResult, ProverState, Unit, UnitToBytes, VerifierState,
7};
8
9pub trait PoWDomainSeparator {
11 #[must_use]
22 fn challenge_pow(self, label: &str) -> Self;
23}
24
25impl<DomainSeparator> PoWDomainSeparator for DomainSeparator
26where
27 DomainSeparator: ByteDomainSeparator,
28{
29 fn challenge_pow(self, label: &str) -> Self {
30 self.challenge_bytes(32, label).add_bytes(8, "pow-nonce")
32 }
33}
34
35pub trait PoWChallenge {
36 fn challenge_pow<S: PowStrategy>(&mut self, bits: f64) -> ProofResult<()>;
38}
39
40impl<H, U, R> PoWChallenge for ProverState<H, U, R>
41where
42 U: Unit,
43 H: DuplexSpongeInterface<U>,
44 R: rand::CryptoRng + rand::RngCore,
45 Self: BytesToUnitSerialize + UnitToBytes,
46{
47 fn challenge_pow<S: PowStrategy>(&mut self, bits: f64) -> ProofResult<()> {
48 let challenge = self.challenge_bytes()?;
49 let nonce = S::new(challenge, bits)
50 .solve()
51 .ok_or(ProofError::InvalidProof)?;
52 self.add_bytes(&nonce.to_be_bytes())?;
53 Ok(())
54 }
55}
56
57impl<H, U> PoWChallenge for VerifierState<'_, H, U>
58where
59 U: Unit,
60 H: DuplexSpongeInterface<U>,
61 Self: BytesToUnitDeserialize + UnitToBytes,
62{
63 fn challenge_pow<S: PowStrategy>(&mut self, bits: f64) -> ProofResult<()> {
64 let challenge = self.challenge_bytes()?;
65 let nonce = u64::from_be_bytes(self.next_bytes()?);
66 if S::new(challenge, bits).check(nonce) {
67 Ok(())
68 } else {
69 Err(ProofError::InvalidProof)
70 }
71 }
72}
73
74pub trait PowStrategy: Clone + Sync {
75 fn new(challenge: [u8; 32], bits: f64) -> Self;
80
81 fn check(&mut self, nonce: u64) -> bool;
83
84 #[cfg(not(feature = "parallel"))]
86 fn solve(&mut self) -> Option<u64> {
87 (0..=u64::MAX).find(|&nonce| self.check(nonce))
89 }
90
91 #[cfg(feature = "parallel")]
92 fn solve(&mut self) -> Option<u64> {
93 use std::sync::atomic::{AtomicU64, Ordering};
97
98 use rayon::broadcast;
99 let global_min = AtomicU64::new(u64::MAX);
100 let _ = broadcast(|ctx| {
101 let mut worker = self.clone();
102 let nonces = (ctx.index() as u64..).step_by(ctx.num_threads());
103 for nonce in nonces {
104 if nonce >= global_min.load(Ordering::Relaxed) {
107 break;
108 }
109 if worker.check(nonce) {
110 global_min.fetch_min(nonce, Ordering::SeqCst);
113 break;
114 }
115 }
116 });
117 match global_min.load(Ordering::SeqCst) {
118 u64::MAX => self.check(u64::MAX).then_some(u64::MAX),
119 nonce => Some(nonce),
120 }
121 }
122}