Files
pezkuwi-subxt/substrate/primitives/state-machine/src/changes_trie/surface_iterator.rs
T
Bastian Köcher 7b56ab15b4 Run cargo fmt on the whole code base (#9394)
* Run cargo fmt on the whole code base

* Second run

* Add CI check

* Fix compilation

* More unnecessary braces

* Handle weights

* Use --all

* Use correct attributes...

* Fix UI tests

* AHHHHHHHHH

* 🤦

* Docs

* Fix compilation

* 🤷

* Please stop

* 🤦 x 2

* More

* make rustfmt.toml consistent with polkadot

Co-authored-by: André Silva <andrerfosilva@gmail.com>
2021-07-21 14:32:32 +00:00

327 lines
9.3 KiB
Rust

// This file is part of Substrate.
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The best way to understand how this iterator works is to imagine some 2D terrain that have some mountains
//! (digest changes tries) and valleys (changes tries for regular blocks). There are gems (blocks) beneath the
//! terrain. Given the request to find all gems in the range [X1; X2] this iterator will return **minimal set**
//! of points at the terrain (mountains and valleys) inside this range that have to be drilled down to
//! search for gems.
use crate::changes_trie::{BlockNumber, ConfigurationRange};
use num_traits::One;
/// Returns surface iterator for given range of blocks.
///
/// `max` is the number of best block, known to caller. We can't access any changes tries
/// that are built after this block, even though we may have them built already.
pub fn surface_iterator<'a, Number: BlockNumber>(
config: ConfigurationRange<'a, Number>,
max: Number,
begin: Number,
end: Number,
) -> Result<SurfaceIterator<'a, Number>, String> {
let (current, current_begin, digest_step, digest_level) =
lower_bound_max_digest(config.clone(), max.clone(), begin.clone(), end)?;
Ok(SurfaceIterator {
config,
begin,
max,
current: Some(current),
current_begin,
digest_step,
digest_level,
})
}
/// Surface iterator - only traverses top-level digests from given range and tries to find
/// all valid digest changes.
///
/// Iterator item is the tuple of (last block of the current point + digest level of the current point).
/// Digest level is Some(0) when it is regular block, is Some(non-zero) when it is digest block and None
/// if it is skewed digest block.
pub struct SurfaceIterator<'a, Number: BlockNumber> {
config: ConfigurationRange<'a, Number>,
begin: Number,
max: Number,
current: Option<Number>,
current_begin: Number,
digest_step: u32,
digest_level: Option<u32>,
}
impl<'a, Number: BlockNumber> Iterator for SurfaceIterator<'a, Number> {
type Item = Result<(Number, Option<u32>), String>;
fn next(&mut self) -> Option<Self::Item> {
let current = self.current.clone()?;
let digest_level = self.digest_level;
if current < self.digest_step.into() {
self.current = None;
} else {
let next = current.clone() - self.digest_step.into();
if next.is_zero() || next < self.begin {
self.current = None;
} else if next > self.current_begin {
self.current = Some(next);
} else {
let max_digest_interval = lower_bound_max_digest(
self.config.clone(),
self.max.clone(),
self.begin.clone(),
next,
);
let (current, current_begin, digest_step, digest_level) = match max_digest_interval
{
Err(err) => return Some(Err(err)),
Ok(range) => range,
};
self.current = Some(current);
self.current_begin = current_begin;
self.digest_step = digest_step;
self.digest_level = digest_level;
}
}
Some(Ok((current, digest_level)))
}
}
/// Returns parameters of highest level digest block that includes the end of given range
/// and tends to include the whole range.
fn lower_bound_max_digest<'a, Number: BlockNumber>(
config: ConfigurationRange<'a, Number>,
max: Number,
begin: Number,
end: Number,
) -> Result<(Number, Number, u32, Option<u32>), String> {
if end > max || begin > end {
return Err(format!("invalid changes range: {}..{}/{}", begin, end, max))
}
if begin <= config.zero ||
config.end.as_ref().map(|config_end| end > *config_end).unwrap_or(false)
{
return Err(format!(
"changes trie range is not covered by configuration: {}..{}/{}..{}",
begin,
end,
config.zero,
match config.end.as_ref() {
Some(config_end) => format!("{}", config_end),
None => "None".into(),
}
))
}
let mut digest_level = 0u32;
let mut digest_step = 1u32;
let mut digest_interval = 0u32;
let mut current = end.clone();
let mut current_begin = begin.clone();
if current_begin != current {
while digest_level != config.config.digest_levels {
// try to use next level digest
let new_digest_level = digest_level + 1;
let new_digest_step = digest_step * config.config.digest_interval;
let new_digest_interval = config.config.digest_interval * {
if digest_interval == 0 {
1
} else {
digest_interval
}
};
let new_digest_begin = config.zero.clone() +
((current.clone() - One::one() - config.zero.clone()) /
new_digest_interval.into()) *
new_digest_interval.into();
let new_digest_end = new_digest_begin.clone() + new_digest_interval.into();
let new_current = new_digest_begin.clone() + new_digest_interval.into();
// check if we met skewed digest
if let Some(skewed_digest_end) = config.end.as_ref() {
if new_digest_end > *skewed_digest_end {
let skewed_digest_start = config.config.prev_max_level_digest_block(
config.zero.clone(),
skewed_digest_end.clone(),
);
if let Some(skewed_digest_start) = skewed_digest_start {
let skewed_digest_range = (skewed_digest_end.clone() -
skewed_digest_start.clone())
.try_into()
.ok()
.expect(
"skewed digest range is always <= max level digest range;\
max level digest range always fits u32; qed",
);
return Ok((
skewed_digest_end.clone(),
skewed_digest_start,
skewed_digest_range,
None,
))
}
}
}
// we can't use next level digest if it touches any unknown (> max) blocks
if new_digest_end > max {
if begin < new_digest_begin {
current_begin = new_digest_begin;
}
break
}
// we can (and will) use this digest
digest_level = new_digest_level;
digest_step = new_digest_step;
digest_interval = new_digest_interval;
current = new_current;
current_begin = new_digest_begin;
// if current digest covers the whole range => no need to use next level digest
if current_begin <= begin && new_digest_end >= end {
break
}
}
}
Ok((current, current_begin, digest_step, Some(digest_level)))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::changes_trie::Configuration;
fn configuration_range<'a>(
config: &'a Configuration,
zero: u64,
) -> ConfigurationRange<'a, u64> {
ConfigurationRange { config, zero, end: None }
}
#[test]
fn lower_bound_max_digest_works() {
let config = Configuration { digest_interval: 4, digest_levels: 2 };
// when config activates at 0
assert_eq!(
lower_bound_max_digest(configuration_range(&config, 0u64), 100_000u64, 20u64, 180u64)
.unwrap(),
(192, 176, 16, Some(2)),
);
// when config activates at 30
assert_eq!(
lower_bound_max_digest(configuration_range(&config, 30u64), 100_000u64, 50u64, 210u64)
.unwrap(),
(222, 206, 16, Some(2)),
);
}
#[test]
fn surface_iterator_works() {
let config = Configuration { digest_interval: 4, digest_levels: 2 };
// when config activates at 0
assert_eq!(
surface_iterator(configuration_range(&config, 0u64), 100_000u64, 40u64, 180u64,)
.unwrap()
.collect::<Vec<_>>(),
vec![
Ok((192, Some(2))),
Ok((176, Some(2))),
Ok((160, Some(2))),
Ok((144, Some(2))),
Ok((128, Some(2))),
Ok((112, Some(2))),
Ok((96, Some(2))),
Ok((80, Some(2))),
Ok((64, Some(2))),
Ok((48, Some(2))),
],
);
// when config activates at 30
assert_eq!(
surface_iterator(configuration_range(&config, 30u64), 100_000u64, 40u64, 180u64,)
.unwrap()
.collect::<Vec<_>>(),
vec![
Ok((190, Some(2))),
Ok((174, Some(2))),
Ok((158, Some(2))),
Ok((142, Some(2))),
Ok((126, Some(2))),
Ok((110, Some(2))),
Ok((94, Some(2))),
Ok((78, Some(2))),
Ok((62, Some(2))),
Ok((46, Some(2))),
],
);
// when config activates at 0 AND max block is before next digest
assert_eq!(
surface_iterator(configuration_range(&config, 0u64), 183u64, 40u64, 183u64)
.unwrap()
.collect::<Vec<_>>(),
vec![
Ok((183, Some(0))),
Ok((182, Some(0))),
Ok((181, Some(0))),
Ok((180, Some(1))),
Ok((176, Some(2))),
Ok((160, Some(2))),
Ok((144, Some(2))),
Ok((128, Some(2))),
Ok((112, Some(2))),
Ok((96, Some(2))),
Ok((80, Some(2))),
Ok((64, Some(2))),
Ok((48, Some(2))),
],
);
}
#[test]
fn surface_iterator_works_with_skewed_digest() {
let config = Configuration { digest_interval: 4, digest_levels: 2 };
let mut config_range = configuration_range(&config, 0u64);
// when config activates at 0 AND ends at 170
config_range.end = Some(170);
assert_eq!(
surface_iterator(config_range, 100_000u64, 40u64, 170u64)
.unwrap()
.collect::<Vec<_>>(),
vec![
Ok((170, None)),
Ok((160, Some(2))),
Ok((144, Some(2))),
Ok((128, Some(2))),
Ok((112, Some(2))),
Ok((96, Some(2))),
Ok((80, Some(2))),
Ok((64, Some(2))),
Ok((48, Some(2))),
],
);
}
}