1use parking_lot::Mutex;
22
23use crate::ProofSizeProvider;
24use std::{collections::VecDeque, sync::Arc};
25
26sp_externalities::decl_extension! {
27 pub struct ProofSizeExt(Box<dyn ProofSizeProvider + 'static + Sync + Send>);
30
31 impl ProofSizeExt {
32 fn start_transaction(&mut self, ty: sp_externalities::TransactionType) {
33 self.0.start_transaction(ty.is_host());
34 }
35
36 fn rollback_transaction(&mut self, ty: sp_externalities::TransactionType) {
37 self.0.rollback_transaction(ty.is_host());
38 }
39
40 fn commit_transaction(&mut self, ty: sp_externalities::TransactionType) {
41 self.0.commit_transaction(ty.is_host());
42 }
43 }
44}
45
46impl ProofSizeExt {
47 pub fn new<T: ProofSizeProvider + Sync + Send + 'static>(recorder: T) -> Self {
49 ProofSizeExt(Box::new(recorder))
50 }
51
52 pub fn storage_proof_size(&self) -> u64 {
54 self.0.estimate_encoded_size() as _
55 }
56}
57
58pub struct RecordedProofSizeEstimations(pub VecDeque<usize>);
64
65impl From<Vec<u32>> for RecordedProofSizeEstimations {
66 fn from(recordings: Vec<u32>) -> Self {
67 Self(recordings.into_iter().map(|x| x as usize).collect())
68 }
69}
70
71struct RecordingProofSizeProviderInner {
73 inner: Box<dyn ProofSizeProvider + Send + Sync>,
74 proof_size_estimations: Vec<Vec<usize>>,
78}
79
80#[derive(Clone)]
86pub struct RecordingProofSizeProvider {
87 inner: Arc<Mutex<RecordingProofSizeProviderInner>>,
88}
89
90impl RecordingProofSizeProvider {
91 pub fn new<T: ProofSizeProvider + Sync + Send + 'static>(recorder: T) -> Self {
93 Self {
94 inner: Arc::new(Mutex::new(RecordingProofSizeProviderInner {
95 inner: Box::new(recorder),
96 proof_size_estimations: vec![Vec::new()],
98 })),
99 }
100 }
101
102 pub fn recorded_estimations(&self) -> Vec<usize> {
105 self.inner.lock().proof_size_estimations.iter().flatten().copied().collect()
106 }
107}
108
109impl ProofSizeProvider for RecordingProofSizeProvider {
110 fn estimate_encoded_size(&self) -> usize {
111 let mut inner = self.inner.lock();
112
113 let estimation = inner.inner.estimate_encoded_size();
114
115 inner
116 .proof_size_estimations
117 .last_mut()
118 .expect("There is always at least one transaction open; qed")
119 .push(estimation);
120
121 estimation
122 }
123
124 fn start_transaction(&mut self, is_host: bool) {
125 if is_host {
139 self.inner.lock().proof_size_estimations.push(Default::default());
140 }
141 }
142
143 fn rollback_transaction(&mut self, is_host: bool) {
144 let mut inner = self.inner.lock();
145
146 if is_host && inner.proof_size_estimations.len() > 1 {
150 inner.proof_size_estimations.pop();
151 }
152 }
153
154 fn commit_transaction(&mut self, is_host: bool) {
155 let mut inner = self.inner.lock();
156
157 if is_host && inner.proof_size_estimations.len() > 1 {
158 let last = inner
159 .proof_size_estimations
160 .pop()
161 .expect("There are more than one element in the vector; qed");
162
163 inner
164 .proof_size_estimations
165 .last_mut()
166 .expect("There are more than one element in the vector; qed")
167 .extend(last);
168 }
169 }
170}
171
172pub struct ReplayProofSizeProvider(Arc<Mutex<RecordedProofSizeEstimations>>);
178
179impl ReplayProofSizeProvider {
180 pub fn from_recorded(recorded: RecordedProofSizeEstimations) -> Self {
182 Self(Arc::new(Mutex::new(recorded)))
183 }
184}
185
186impl From<RecordedProofSizeEstimations> for ReplayProofSizeProvider {
187 fn from(value: RecordedProofSizeEstimations) -> Self {
188 Self::from_recorded(value)
189 }
190}
191
192impl ProofSizeProvider for ReplayProofSizeProvider {
193 fn estimate_encoded_size(&self) -> usize {
194 self.0.lock().0.pop_front().unwrap_or_default()
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201 use std::sync::atomic::{AtomicUsize, Ordering};
202
203 #[derive(Clone)]
205 struct MockProofSizeProvider {
206 size: Arc<AtomicUsize>,
207 }
208
209 impl MockProofSizeProvider {
210 fn new(initial_size: usize) -> Self {
211 Self { size: Arc::new(AtomicUsize::new(initial_size)) }
212 }
213
214 fn set_size(&self, new_size: usize) {
215 self.size.store(new_size, Ordering::Relaxed);
216 }
217 }
218
219 impl ProofSizeProvider for MockProofSizeProvider {
220 fn estimate_encoded_size(&self) -> usize {
221 self.size.load(Ordering::Relaxed)
222 }
223
224 fn start_transaction(&mut self, _is_host: bool) {}
225 fn rollback_transaction(&mut self, _is_host: bool) {}
226 fn commit_transaction(&mut self, _is_host: bool) {}
227 }
228
229 #[test]
230 fn recording_proof_size_provider_basic_functionality() {
231 let mock = MockProofSizeProvider::new(100);
232 let tracker = RecordingProofSizeProvider::new(mock.clone());
233
234 assert_eq!(tracker.recorded_estimations(), Vec::<usize>::new());
236
237 let size = tracker.estimate_encoded_size();
239 assert_eq!(size, 100);
240 assert_eq!(tracker.recorded_estimations(), vec![100]);
241
242 mock.set_size(200);
244 let size = tracker.estimate_encoded_size();
245 assert_eq!(size, 200);
246 assert_eq!(tracker.recorded_estimations(), vec![100, 200]);
247
248 let size = tracker.estimate_encoded_size();
250 assert_eq!(size, 200);
251 assert_eq!(tracker.recorded_estimations(), vec![100, 200, 200]);
252 }
253
254 #[test]
255 fn recording_proof_size_provider_host_transactions() {
256 let mock = MockProofSizeProvider::new(100);
257 let mut tracker = RecordingProofSizeProvider::new(mock.clone());
258
259 tracker.estimate_encoded_size();
261 tracker.estimate_encoded_size();
262 assert_eq!(tracker.recorded_estimations(), vec![100, 100]);
263
264 tracker.start_transaction(true);
266 mock.set_size(200);
267 tracker.estimate_encoded_size();
268
269 assert_eq!(tracker.recorded_estimations(), vec![100, 100, 200]);
271
272 tracker.commit_transaction(true);
274
275 assert_eq!(tracker.recorded_estimations(), vec![100, 100, 200]);
277
278 mock.set_size(300);
280 tracker.estimate_encoded_size();
281 assert_eq!(tracker.recorded_estimations(), vec![100, 100, 200, 300]);
282 }
283
284 #[test]
285 fn recording_proof_size_provider_host_transaction_rollback() {
286 let mock = MockProofSizeProvider::new(100);
287 let mut tracker = RecordingProofSizeProvider::new(mock.clone());
288
289 tracker.estimate_encoded_size();
291 assert_eq!(tracker.recorded_estimations(), vec![100]);
292
293 tracker.start_transaction(true);
295 mock.set_size(200);
296 tracker.estimate_encoded_size();
297 tracker.estimate_encoded_size();
298
299 assert_eq!(tracker.recorded_estimations(), vec![100, 200, 200]);
301
302 tracker.rollback_transaction(true);
304
305 assert_eq!(tracker.recorded_estimations(), vec![100]);
307 }
308
309 #[test]
310 fn recording_proof_size_provider_runtime_transactions_ignored() {
311 let mock = MockProofSizeProvider::new(100);
312 let mut tracker = RecordingProofSizeProvider::new(mock.clone());
313
314 tracker.estimate_encoded_size();
316 assert_eq!(tracker.recorded_estimations(), vec![100]);
317
318 tracker.start_transaction(false);
320 mock.set_size(200);
321 tracker.estimate_encoded_size();
322
323 assert_eq!(tracker.recorded_estimations(), vec![100, 200]);
325
326 tracker.commit_transaction(false);
328 assert_eq!(tracker.recorded_estimations(), vec![100, 200]);
329
330 tracker.rollback_transaction(false);
332 assert_eq!(tracker.recorded_estimations(), vec![100, 200]);
333 }
334
335 #[test]
336 fn recording_proof_size_provider_nested_host_transactions() {
337 let mock = MockProofSizeProvider::new(100);
338 let mut tracker = RecordingProofSizeProvider::new(mock.clone());
339
340 tracker.estimate_encoded_size();
342 assert_eq!(tracker.recorded_estimations(), vec![100]);
343
344 tracker.start_transaction(true);
346 mock.set_size(200);
347 tracker.estimate_encoded_size();
348
349 tracker.start_transaction(true);
351 mock.set_size(300);
352 tracker.estimate_encoded_size();
353
354 assert_eq!(tracker.recorded_estimations(), vec![100, 200, 300]);
355
356 tracker.commit_transaction(true);
358 assert_eq!(tracker.recorded_estimations(), vec![100, 200, 300]);
359
360 tracker.commit_transaction(true);
362 assert_eq!(tracker.recorded_estimations(), vec![100, 200, 300]);
363 }
364
365 #[test]
366 fn recording_proof_size_provider_nested_host_transaction_rollback() {
367 let mock = MockProofSizeProvider::new(100);
368 let mut tracker = RecordingProofSizeProvider::new(mock.clone());
369
370 tracker.estimate_encoded_size();
372
373 tracker.start_transaction(true);
375 mock.set_size(200);
376 tracker.estimate_encoded_size();
377
378 tracker.start_transaction(true);
380 mock.set_size(300);
381 tracker.estimate_encoded_size();
382
383 assert_eq!(tracker.recorded_estimations(), vec![100, 200, 300]);
384
385 tracker.rollback_transaction(true);
387 assert_eq!(tracker.recorded_estimations(), vec![100, 200]);
388
389 tracker.rollback_transaction(true);
391 assert_eq!(tracker.recorded_estimations(), vec![100]);
392 }
393
394 #[test]
395 fn recording_proof_size_provider_rollback_on_base_transaction_does_nothing() {
396 let mock = MockProofSizeProvider::new(100);
397 let mut tracker = RecordingProofSizeProvider::new(mock.clone());
398
399 tracker.estimate_encoded_size();
401 tracker.estimate_encoded_size();
402 assert_eq!(tracker.recorded_estimations(), vec![100, 100]);
403
404 tracker.rollback_transaction(true);
406 assert_eq!(tracker.recorded_estimations(), vec![100, 100]);
407 }
408
409 #[test]
410 fn recorded_proof_size_estimations_struct() {
411 let estimations = vec![100, 200, 300];
412 let recorded = RecordedProofSizeEstimations(estimations.into());
413 let expected: VecDeque<usize> = vec![100, 200, 300].into();
414 assert_eq!(recorded.0, expected);
415 }
416
417 #[test]
418 fn replay_proof_size_provider_basic_functionality() {
419 let estimations = vec![100, 200, 300, 150];
420 let recorded = RecordedProofSizeEstimations(estimations.into());
421 let replay = ReplayProofSizeProvider::from_recorded(recorded);
422
423 assert_eq!(replay.estimate_encoded_size(), 100);
425 assert_eq!(replay.estimate_encoded_size(), 200);
426 assert_eq!(replay.estimate_encoded_size(), 300);
427 assert_eq!(replay.estimate_encoded_size(), 150);
428 }
429
430 #[test]
431 fn replay_proof_size_provider_exhausted_returns_zero() {
432 let estimations = vec![100, 200];
433 let recorded = RecordedProofSizeEstimations(estimations.into());
434 let replay = ReplayProofSizeProvider::from_recorded(recorded);
435
436 assert_eq!(replay.estimate_encoded_size(), 100);
438 assert_eq!(replay.estimate_encoded_size(), 200);
439
440 assert_eq!(replay.estimate_encoded_size(), 0);
442 assert_eq!(replay.estimate_encoded_size(), 0);
443 }
444
445 #[test]
446 fn replay_proof_size_provider_empty_returns_zero() {
447 let recorded = RecordedProofSizeEstimations(VecDeque::new());
448 let replay = ReplayProofSizeProvider::from_recorded(recorded);
449
450 assert_eq!(replay.estimate_encoded_size(), 0);
452 assert_eq!(replay.estimate_encoded_size(), 0);
453 }
454
455 #[test]
456 fn replay_proof_size_provider_from_trait() {
457 let estimations = vec![42, 84];
458 let recorded = RecordedProofSizeEstimations(estimations.into());
459 let replay: ReplayProofSizeProvider = recorded.into();
460
461 assert_eq!(replay.estimate_encoded_size(), 42);
462 assert_eq!(replay.estimate_encoded_size(), 84);
463 assert_eq!(replay.estimate_encoded_size(), 0);
464 }
465
466 #[test]
467 fn record_and_replay_integration() {
468 let mock = MockProofSizeProvider::new(100);
469 let recorder = RecordingProofSizeProvider::new(mock.clone());
470
471 recorder.estimate_encoded_size();
473 mock.set_size(200);
474 recorder.estimate_encoded_size();
475 mock.set_size(300);
476 recorder.estimate_encoded_size();
477
478 let recorded_estimations = recorder.recorded_estimations();
480 assert_eq!(recorded_estimations, vec![100, 200, 300]);
481
482 let recorded = RecordedProofSizeEstimations(recorded_estimations.into());
484 let replay = ReplayProofSizeProvider::from_recorded(recorded);
485
486 assert_eq!(replay.estimate_encoded_size(), 100);
488 assert_eq!(replay.estimate_encoded_size(), 200);
489 assert_eq!(replay.estimate_encoded_size(), 300);
490 assert_eq!(replay.estimate_encoded_size(), 0);
491 }
492
493 #[test]
494 fn replay_proof_size_provider_single_value() {
495 let estimations = vec![42];
496 let recorded = RecordedProofSizeEstimations(estimations.into());
497 let replay = ReplayProofSizeProvider::from_recorded(recorded);
498
499 assert_eq!(replay.estimate_encoded_size(), 42);
501 assert_eq!(replay.estimate_encoded_size(), 0);
502 }
503}