1#[cfg(test)]
4mod tests;
5
6use crate::PotNextSlotInput;
7use ab_core_primitives::pot::{
8 PotCheckpoints, PotOutput, PotParametersChange, PotSeed, SlotNumber,
9};
10use parking_lot::Mutex;
11use schnellru::{ByLength, LruMap};
12use std::num::NonZeroU32;
13use std::sync::Arc;
14
15#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
16struct CacheKey {
17 seed: PotSeed,
18 slot_iterations: NonZeroU32,
19}
20
21#[derive(Debug, Clone)]
22struct CacheValue {
23 checkpoints: Arc<Mutex<Option<PotCheckpoints>>>,
24}
25
26#[derive(Debug, Clone)]
28pub struct PotVerifier {
29 genesis_seed: PotSeed,
30 cache: Arc<Mutex<LruMap<CacheKey, CacheValue>>>,
31}
32
33impl PotVerifier {
34 pub fn new(genesis_seed: PotSeed, cache_size: u32) -> Self {
35 Self {
36 genesis_seed,
37 cache: Arc::new(Mutex::new(LruMap::new(ByLength::new(cache_size)))),
38 }
39 }
40
41 pub fn inject_verified_checkpoints(
43 &self,
44 seed: PotSeed,
45 slot_iterations: NonZeroU32,
46 checkpoints: PotCheckpoints,
47 ) {
48 self.cache.lock().insert(
49 CacheKey {
50 seed,
51 slot_iterations,
52 },
53 CacheValue {
54 checkpoints: Arc::new(Mutex::new(Some(checkpoints))),
55 },
56 );
57 }
58
59 pub fn genesis_seed(&self) -> PotSeed {
61 self.genesis_seed
62 }
63
64 pub fn get_checkpoints(
67 &self,
68 slot_iterations: NonZeroU32,
69 seed: PotSeed,
70 ) -> Option<PotCheckpoints> {
71 self.calculate_checkpoints(slot_iterations, seed, true)
72 }
73
74 pub fn try_get_checkpoints(
76 &self,
77 slot_iterations: NonZeroU32,
78 seed: PotSeed,
79 ) -> Option<PotCheckpoints> {
80 let cache_key = CacheKey {
81 seed,
82 slot_iterations,
83 };
84
85 self.cache
86 .lock()
87 .get(&cache_key)
88 .and_then(|value| value.checkpoints.try_lock()?.as_ref().copied())
89 }
90
91 pub fn is_output_valid(
101 &self,
102 input: PotNextSlotInput,
103 slots: SlotNumber,
104 output: PotOutput,
105 maybe_parameters_change: Option<PotParametersChange>,
106 ) -> bool {
107 self.is_output_valid_internal(input, slots, output, maybe_parameters_change, true)
108 }
109
110 pub fn try_is_output_valid(
114 &self,
115 input: PotNextSlotInput,
116 slots: SlotNumber,
117 output: PotOutput,
118 maybe_parameters_change: Option<PotParametersChange>,
119 ) -> bool {
120 self.is_output_valid_internal(input, slots, output, maybe_parameters_change, false)
121 }
122
123 fn is_output_valid_internal(
124 &self,
125 mut input: PotNextSlotInput,
126 slots: SlotNumber,
127 output: PotOutput,
128 maybe_parameters_change: Option<PotParametersChange>,
129 do_proving_if_necessary: bool,
130 ) -> bool {
131 let mut slots = u64::from(slots);
132
133 loop {
134 let maybe_calculated_checkpoints = self.calculate_checkpoints(
135 input.slot_iterations,
136 input.seed,
137 do_proving_if_necessary,
138 );
139
140 let Some(calculated_checkpoints) = maybe_calculated_checkpoints else {
141 return false;
142 };
143 let calculated_output = calculated_checkpoints.output();
144
145 slots -= 1;
146
147 if slots == 0 {
148 return output == calculated_output;
149 }
150
151 input = PotNextSlotInput::derive(
152 input.slot_iterations,
153 input.slot,
154 calculated_output,
155 &maybe_parameters_change,
156 );
157 }
158 }
159
160 fn calculate_checkpoints(
162 &self,
163 slot_iterations: NonZeroU32,
164 seed: PotSeed,
165 do_proving_if_necessary: bool,
166 ) -> Option<PotCheckpoints> {
167 let cache_key = CacheKey {
168 seed,
169 slot_iterations,
170 };
171
172 loop {
173 let mut cache = self.cache.lock();
174 let maybe_cache_value = cache.get(&cache_key).cloned();
175 if let Some(cache_value) = maybe_cache_value {
176 drop(cache);
177 let correct_checkpoints = cache_value.checkpoints.lock();
178 if let Some(correct_checkpoints) = *correct_checkpoints {
179 return Some(correct_checkpoints);
180 }
181
182 continue;
184 }
185
186 if !do_proving_if_necessary {
187 return None;
189 }
190
191 let cache_value = CacheValue {
192 checkpoints: Arc::default(),
193 };
194 let checkpoints = Arc::clone(&cache_value.checkpoints);
195 let mut checkpoints = checkpoints
197 .try_lock()
198 .expect("No one can access this mutex yet; qed");
199 cache.insert(cache_key, cache_value);
201 drop(cache);
203
204 let proving_result = ab_proof_of_time::prove(seed, slot_iterations);
205
206 let Ok(generated_checkpoints) = proving_result else {
207 drop(checkpoints);
209
210 let maybe_removed_cache_value = self.cache.lock().remove(&cache_key);
212 if let Some(removed_cache_value) = maybe_removed_cache_value {
213 let removed_verified_value = removed_cache_value.checkpoints.lock().is_some();
216 if removed_verified_value {
217 self.cache.lock().insert(cache_key, removed_cache_value);
218 }
219 }
220 return None;
221 };
222
223 checkpoints.replace(generated_checkpoints);
224 return Some(generated_checkpoints);
225 }
226 }
227
228 pub fn verify_checkpoints(
230 &self,
231 seed: PotSeed,
232 slot_iterations: NonZeroU32,
233 checkpoints: &PotCheckpoints,
234 ) -> bool {
235 self.verify_checkpoints_internal(seed, slot_iterations, checkpoints)
236 }
237
238 fn verify_checkpoints_internal(
239 &self,
240 seed: PotSeed,
241 slot_iterations: NonZeroU32,
242 checkpoints: &PotCheckpoints,
243 ) -> bool {
244 let cache_key = CacheKey {
245 seed,
246 slot_iterations,
247 };
248
249 loop {
250 let mut cache = self.cache.lock();
251 if let Some(cache_value) = cache.get(&cache_key).cloned() {
252 drop(cache);
253 let correct_checkpoints = cache_value.checkpoints.lock();
254 if let Some(correct_checkpoints) = correct_checkpoints.as_ref() {
255 return checkpoints == correct_checkpoints;
256 }
257
258 continue;
260 }
261
262 let cache_value = CacheValue {
263 checkpoints: Arc::default(),
264 };
265 let correct_checkpoints = Arc::clone(&cache_value.checkpoints);
266 let mut correct_checkpoints = correct_checkpoints
268 .try_lock()
269 .expect("No one can access this mutex yet; qed");
270 cache.insert(cache_key, cache_value);
272 drop(cache);
274
275 let verified_successfully =
276 ab_proof_of_time::verify(seed, slot_iterations, checkpoints).unwrap_or_default();
277
278 if !verified_successfully {
279 drop(correct_checkpoints);
281
282 let maybe_removed_cache_value = self.cache.lock().remove(&cache_key);
284 if let Some(removed_cache_value) = maybe_removed_cache_value {
285 let removed_verified_value = removed_cache_value.checkpoints.lock().is_some();
288 if removed_verified_value {
289 self.cache.lock().insert(cache_key, removed_cache_value);
290 }
291 }
292 return false;
293 }
294
295 correct_checkpoints.replace(*checkpoints);
297
298 return true;
299 }
300 }
301}