#[cfg(any(feature = "std", test))]
use serde::{Serialize, Deserialize};
use parity_codec::{Encode, Decode};
#[cfg_attr(any(feature = "std", test), derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)]
pub struct ChangesTrieConfiguration {
pub digest_interval: u64,
pub digest_levels: u32,
}
impl ChangesTrieConfiguration {
pub fn is_digest_build_enabled(&self) -> bool {
self.digest_interval > 1 && self.digest_levels > 0
}
pub fn is_digest_build_required_at_block(&self, block: u64) -> bool {
block != 0
&& self.is_digest_build_enabled()
&& block % self.digest_interval == 0
}
pub fn max_digest_interval(&self) -> u64 {
if !self.is_digest_build_enabled() {
return 1;
}
let mut max_digest_interval = self.digest_interval;
for _ in 1..self.digest_levels {
max_digest_interval = match max_digest_interval.checked_mul(self.digest_interval) {
Some(max_digest_interval) => max_digest_interval,
None => return u64::max_value(),
}
}
max_digest_interval
}
pub fn digest_level_at_block(&self, block: u64) -> Option<(u32, u64, u64)> {
if !self.is_digest_build_required_at_block(block) {
return None;
}
let mut digest_interval = self.digest_interval;
let mut current_level = 1u32;
let mut digest_step = 1u64;
while current_level < self.digest_levels {
let new_digest_interval = match digest_interval.checked_mul(self.digest_interval) {
Some(new_digest_interval) if block % new_digest_interval == 0 => new_digest_interval,
_ => break,
};
digest_step = digest_interval;
digest_interval = new_digest_interval;
current_level = current_level + 1;
}
Some((
current_level,
digest_interval,
digest_step,
))
}
}
#[cfg(test)]
mod tests {
use super::ChangesTrieConfiguration;
fn config(interval: u64, levels: u32) -> ChangesTrieConfiguration {
ChangesTrieConfiguration {
digest_interval: interval,
digest_levels: levels,
}
}
#[test]
fn is_digest_build_enabled_works() {
assert!(!config(0, 100).is_digest_build_enabled());
assert!(!config(1, 100).is_digest_build_enabled());
assert!(config(2, 100).is_digest_build_enabled());
assert!(!config(100, 0).is_digest_build_enabled());
assert!(config(100, 1).is_digest_build_enabled());
}
#[test]
fn is_digest_build_required_at_block_works() {
assert!(!config(8, 4).is_digest_build_required_at_block(0));
assert!(!config(8, 4).is_digest_build_required_at_block(1));
assert!(!config(8, 4).is_digest_build_required_at_block(2));
assert!(!config(8, 4).is_digest_build_required_at_block(4));
assert!(config(8, 4).is_digest_build_required_at_block(8));
assert!(!config(8, 4).is_digest_build_required_at_block(9));
assert!(config(8, 4).is_digest_build_required_at_block(64));
assert!(config(8, 4).is_digest_build_required_at_block(64));
assert!(config(8, 4).is_digest_build_required_at_block(512));
assert!(config(8, 4).is_digest_build_required_at_block(4096));
assert!(!config(8, 4).is_digest_build_required_at_block(4103));
assert!(config(8, 4).is_digest_build_required_at_block(4104));
assert!(!config(8, 4).is_digest_build_required_at_block(4108));
}
#[test]
fn digest_level_at_block_works() {
assert_eq!(config(8, 4).digest_level_at_block(0), None);
assert_eq!(config(8, 4).digest_level_at_block(7), None);
assert_eq!(config(8, 4).digest_level_at_block(63), None);
assert_eq!(config(8, 4).digest_level_at_block(8), Some((1, 8, 1)));
assert_eq!(config(8, 4).digest_level_at_block(64), Some((2, 64, 8)));
assert_eq!(config(8, 4).digest_level_at_block(512), Some((3, 512, 64)));
assert_eq!(config(8, 4).digest_level_at_block(4096), Some((4, 4096, 512)));
assert_eq!(config(8, 4).digest_level_at_block(4112), Some((1, 8, 1)));
}
#[test]
fn max_digest_interval_works() {
assert_eq!(config(0, 0).max_digest_interval(), 1);
assert_eq!(config(2, 2).max_digest_interval(), 4);
assert_eq!(config(8, 4).max_digest_interval(), 4096);
assert_eq!(config(::std::u64::MAX, 1024).max_digest_interval(), ::std::u64::MAX);
}
}