use parking_lot::Mutex;
use super::ProofSizeProvider;
use std::{collections::VecDeque, sync::Arc};
crate::decl_extension! {
pub struct ProofSizeExt(Box<dyn ProofSizeProvider + 'static + Sync + Send>);
impl ProofSizeExt {
fn start_transaction(&mut self, ty: crate::externalities::TransactionType) {
self.0.start_transaction(ty.is_host());
}
fn rollback_transaction(&mut self, ty: crate::externalities::TransactionType) {
self.0.rollback_transaction(ty.is_host());
}
fn commit_transaction(&mut self, ty: crate::externalities::TransactionType) {
self.0.commit_transaction(ty.is_host());
}
}
}
impl ProofSizeExt {
pub fn new<T: ProofSizeProvider + Sync + Send + 'static>(recorder: T) -> Self {
ProofSizeExt(Box::new(recorder))
}
pub fn storage_proof_size(&self) -> u64 {
self.0.estimate_encoded_size() as _
}
}
pub struct RecordedProofSizeEstimations(pub VecDeque<usize>);
struct RecordingProofSizeProviderInner {
inner: Box<dyn ProofSizeProvider + Send + Sync>,
proof_size_estimations: Vec<Vec<usize>>,
}
#[derive(Clone)]
pub struct RecordingProofSizeProvider {
inner: Arc<Mutex<RecordingProofSizeProviderInner>>,
}
impl RecordingProofSizeProvider {
pub fn new<T: ProofSizeProvider + Sync + Send + 'static>(recorder: T) -> Self {
Self {
inner: Arc::new(Mutex::new(RecordingProofSizeProviderInner {
inner: Box::new(recorder),
proof_size_estimations: vec![Vec::new()],
})),
}
}
pub fn recorded_estimations(&self) -> Vec<usize> {
self.inner.lock().proof_size_estimations.iter().flatten().copied().collect()
}
}
impl ProofSizeProvider for RecordingProofSizeProvider {
fn estimate_encoded_size(&self) -> usize {
let mut inner = self.inner.lock();
let estimation = inner.inner.estimate_encoded_size();
inner
.proof_size_estimations
.last_mut()
.expect("There is always at least one transaction open; qed")
.push(estimation);
estimation
}
fn start_transaction(&mut self, is_host: bool) {
if is_host {
self.inner.lock().proof_size_estimations.push(Default::default());
}
}
fn rollback_transaction(&mut self, is_host: bool) {
let mut inner = self.inner.lock();
if is_host && inner.proof_size_estimations.len() > 1 {
inner.proof_size_estimations.pop();
}
}
fn commit_transaction(&mut self, is_host: bool) {
let mut inner = self.inner.lock();
if is_host && inner.proof_size_estimations.len() > 1 {
let last = inner
.proof_size_estimations
.pop()
.expect("There are more than one element in the vector; qed");
inner
.proof_size_estimations
.last_mut()
.expect("There are more than one element in the vector; qed")
.extend(last);
}
}
}
pub struct ReplayProofSizeProvider(Arc<Mutex<RecordedProofSizeEstimations>>);
impl ReplayProofSizeProvider {
pub fn from_recorded(recorded: RecordedProofSizeEstimations) -> Self {
Self(Arc::new(Mutex::new(recorded)))
}
}
impl From<RecordedProofSizeEstimations> for ReplayProofSizeProvider {
fn from(value: RecordedProofSizeEstimations) -> Self {
Self::from_recorded(value)
}
}
impl ProofSizeProvider for ReplayProofSizeProvider {
fn estimate_encoded_size(&self) -> usize {
self.0.lock().0.pop_front().unwrap_or_default()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Clone)]
struct MockProofSizeProvider {
size: Arc<AtomicUsize>,
}
impl MockProofSizeProvider {
fn new(initial_size: usize) -> Self {
Self { size: Arc::new(AtomicUsize::new(initial_size)) }
}
fn set_size(&self, new_size: usize) {
self.size.store(new_size, Ordering::Relaxed);
}
}
impl ProofSizeProvider for MockProofSizeProvider {
fn estimate_encoded_size(&self) -> usize {
self.size.load(Ordering::Relaxed)
}
fn start_transaction(&mut self, _is_host: bool) {}
fn rollback_transaction(&mut self, _is_host: bool) {}
fn commit_transaction(&mut self, _is_host: bool) {}
}
#[test]
fn recording_proof_size_provider_basic_functionality() {
let mock = MockProofSizeProvider::new(100);
let tracker = RecordingProofSizeProvider::new(mock.clone());
assert_eq!(tracker.recorded_estimations(), Vec::<usize>::new());
let size = tracker.estimate_encoded_size();
assert_eq!(size, 100);
assert_eq!(tracker.recorded_estimations(), vec![100]);
mock.set_size(200);
let size = tracker.estimate_encoded_size();
assert_eq!(size, 200);
assert_eq!(tracker.recorded_estimations(), vec![100, 200]);
let size = tracker.estimate_encoded_size();
assert_eq!(size, 200);
assert_eq!(tracker.recorded_estimations(), vec![100, 200, 200]);
}
#[test]
fn recording_proof_size_provider_host_transactions() {
let mock = MockProofSizeProvider::new(100);
let mut tracker = RecordingProofSizeProvider::new(mock.clone());
tracker.estimate_encoded_size();
tracker.estimate_encoded_size();
assert_eq!(tracker.recorded_estimations(), vec![100, 100]);
tracker.start_transaction(true);
mock.set_size(200);
tracker.estimate_encoded_size();
assert_eq!(tracker.recorded_estimations(), vec![100, 100, 200]);
tracker.commit_transaction(true);
assert_eq!(tracker.recorded_estimations(), vec![100, 100, 200]);
mock.set_size(300);
tracker.estimate_encoded_size();
assert_eq!(tracker.recorded_estimations(), vec![100, 100, 200, 300]);
}
#[test]
fn recording_proof_size_provider_host_transaction_rollback() {
let mock = MockProofSizeProvider::new(100);
let mut tracker = RecordingProofSizeProvider::new(mock.clone());
tracker.estimate_encoded_size();
assert_eq!(tracker.recorded_estimations(), vec![100]);
tracker.start_transaction(true);
mock.set_size(200);
tracker.estimate_encoded_size();
tracker.estimate_encoded_size();
assert_eq!(tracker.recorded_estimations(), vec![100, 200, 200]);
tracker.rollback_transaction(true);
assert_eq!(tracker.recorded_estimations(), vec![100]);
}
#[test]
fn recording_proof_size_provider_runtime_transactions_ignored() {
let mock = MockProofSizeProvider::new(100);
let mut tracker = RecordingProofSizeProvider::new(mock.clone());
tracker.estimate_encoded_size();
assert_eq!(tracker.recorded_estimations(), vec![100]);
tracker.start_transaction(false);
mock.set_size(200);
tracker.estimate_encoded_size();
assert_eq!(tracker.recorded_estimations(), vec![100, 200]);
tracker.commit_transaction(false);
assert_eq!(tracker.recorded_estimations(), vec![100, 200]);
tracker.rollback_transaction(false);
assert_eq!(tracker.recorded_estimations(), vec![100, 200]);
}
#[test]
fn recording_proof_size_provider_nested_host_transactions() {
let mock = MockProofSizeProvider::new(100);
let mut tracker = RecordingProofSizeProvider::new(mock.clone());
tracker.estimate_encoded_size();
assert_eq!(tracker.recorded_estimations(), vec![100]);
tracker.start_transaction(true);
mock.set_size(200);
tracker.estimate_encoded_size();
tracker.start_transaction(true);
mock.set_size(300);
tracker.estimate_encoded_size();
assert_eq!(tracker.recorded_estimations(), vec![100, 200, 300]);
tracker.commit_transaction(true);
assert_eq!(tracker.recorded_estimations(), vec![100, 200, 300]);
tracker.commit_transaction(true);
assert_eq!(tracker.recorded_estimations(), vec![100, 200, 300]);
}
#[test]
fn recording_proof_size_provider_nested_host_transaction_rollback() {
let mock = MockProofSizeProvider::new(100);
let mut tracker = RecordingProofSizeProvider::new(mock.clone());
tracker.estimate_encoded_size();
tracker.start_transaction(true);
mock.set_size(200);
tracker.estimate_encoded_size();
tracker.start_transaction(true);
mock.set_size(300);
tracker.estimate_encoded_size();
assert_eq!(tracker.recorded_estimations(), vec![100, 200, 300]);
tracker.rollback_transaction(true);
assert_eq!(tracker.recorded_estimations(), vec![100, 200]);
tracker.rollback_transaction(true);
assert_eq!(tracker.recorded_estimations(), vec![100]);
}
#[test]
fn recording_proof_size_provider_rollback_on_base_transaction_does_nothing() {
let mock = MockProofSizeProvider::new(100);
let mut tracker = RecordingProofSizeProvider::new(mock.clone());
tracker.estimate_encoded_size();
tracker.estimate_encoded_size();
assert_eq!(tracker.recorded_estimations(), vec![100, 100]);
tracker.rollback_transaction(true);
assert_eq!(tracker.recorded_estimations(), vec![100, 100]);
}
#[test]
fn recorded_proof_size_estimations_struct() {
let estimations = vec![100, 200, 300];
let recorded = RecordedProofSizeEstimations(estimations.into());
let expected: VecDeque<usize> = vec![100, 200, 300].into();
assert_eq!(recorded.0, expected);
}
#[test]
fn replay_proof_size_provider_basic_functionality() {
let estimations = vec![100, 200, 300, 150];
let recorded = RecordedProofSizeEstimations(estimations.into());
let replay = ReplayProofSizeProvider::from_recorded(recorded);
assert_eq!(replay.estimate_encoded_size(), 100);
assert_eq!(replay.estimate_encoded_size(), 200);
assert_eq!(replay.estimate_encoded_size(), 300);
assert_eq!(replay.estimate_encoded_size(), 150);
}
#[test]
fn replay_proof_size_provider_exhausted_returns_zero() {
let estimations = vec![100, 200];
let recorded = RecordedProofSizeEstimations(estimations.into());
let replay = ReplayProofSizeProvider::from_recorded(recorded);
assert_eq!(replay.estimate_encoded_size(), 100);
assert_eq!(replay.estimate_encoded_size(), 200);
assert_eq!(replay.estimate_encoded_size(), 0);
assert_eq!(replay.estimate_encoded_size(), 0);
}
#[test]
fn replay_proof_size_provider_empty_returns_zero() {
let recorded = RecordedProofSizeEstimations(VecDeque::new());
let replay = ReplayProofSizeProvider::from_recorded(recorded);
assert_eq!(replay.estimate_encoded_size(), 0);
assert_eq!(replay.estimate_encoded_size(), 0);
}
#[test]
fn replay_proof_size_provider_from_trait() {
let estimations = vec![42, 84];
let recorded = RecordedProofSizeEstimations(estimations.into());
let replay: ReplayProofSizeProvider = recorded.into();
assert_eq!(replay.estimate_encoded_size(), 42);
assert_eq!(replay.estimate_encoded_size(), 84);
assert_eq!(replay.estimate_encoded_size(), 0);
}
#[test]
fn record_and_replay_integration() {
let mock = MockProofSizeProvider::new(100);
let recorder = RecordingProofSizeProvider::new(mock.clone());
recorder.estimate_encoded_size();
mock.set_size(200);
recorder.estimate_encoded_size();
mock.set_size(300);
recorder.estimate_encoded_size();
let recorded_estimations = recorder.recorded_estimations();
assert_eq!(recorded_estimations, vec![100, 200, 300]);
let recorded = RecordedProofSizeEstimations(recorded_estimations.into());
let replay = ReplayProofSizeProvider::from_recorded(recorded);
assert_eq!(replay.estimate_encoded_size(), 100);
assert_eq!(replay.estimate_encoded_size(), 200);
assert_eq!(replay.estimate_encoded_size(), 300);
assert_eq!(replay.estimate_encoded_size(), 0);
}
#[test]
fn replay_proof_size_provider_single_value() {
let estimations = vec![42];
let recorded = RecordedProofSizeEstimations(estimations.into());
let replay = ReplayProofSizeProvider::from_recorded(recorded);
assert_eq!(replay.estimate_encoded_size(), 42);
assert_eq!(replay.estimate_encoded_size(), 0);
}
}