Step duration map configuration parameter ported from the POA Network fork (#10902)
* step duration map configuration parameter ported from POA Network fork * step duration map refactoring * added a test of step duration change * refactoring of vector search; return Err instead of panicking * removed dead code and the Config engine error variant * doc correction * converted triples to struct StepDurationInfo
This commit is contained in:
		
							parent
							
								
									2c97bcc1a4
								
							
						
					
					
						commit
						e0e79fdee0
					
				@ -33,11 +33,12 @@
 | 
			
		||||
 | 
			
		||||
use std::collections::{BTreeMap, BTreeSet, HashSet};
 | 
			
		||||
use std::{cmp, fmt};
 | 
			
		||||
use std::iter::FromIterator;
 | 
			
		||||
use std::iter::{self, FromIterator};
 | 
			
		||||
use std::ops::Deref;
 | 
			
		||||
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
 | 
			
		||||
use std::sync::atomic::{AtomicU64, AtomicBool, Ordering as AtomicOrdering};
 | 
			
		||||
use std::sync::{Weak, Arc};
 | 
			
		||||
use std::time::{UNIX_EPOCH, Duration};
 | 
			
		||||
use std::u64;
 | 
			
		||||
 | 
			
		||||
use client_traits::EngineClient;
 | 
			
		||||
use engine::{Engine, ConstructedVerifier};
 | 
			
		||||
@ -83,12 +84,13 @@ use self::finality::RollingFinality;
 | 
			
		||||
 | 
			
		||||
/// `AuthorityRound` params.
 | 
			
		||||
pub struct AuthorityRoundParams {
 | 
			
		||||
	/// Time to wait before next block or authority switching,
 | 
			
		||||
	/// in seconds.
 | 
			
		||||
	/// A map defining intervals of blocks with the given times (in seconds) to wait before next
 | 
			
		||||
	/// block or authority switching. The keys in the map are steps of starting blocks of those
 | 
			
		||||
	/// periods. The entry at `0` should be defined.
 | 
			
		||||
	///
 | 
			
		||||
	/// Deliberately typed as u16 as too high of a value leads
 | 
			
		||||
	/// to slow block issuance.
 | 
			
		||||
	pub step_duration: u16,
 | 
			
		||||
	/// Wait times (durations) are additionally required to be less than 65535 since larger values
 | 
			
		||||
	/// lead to slow block issuance.
 | 
			
		||||
	pub step_durations: BTreeMap<u64, u64>,
 | 
			
		||||
	/// Starting step,
 | 
			
		||||
	pub start_step: Option<u64>,
 | 
			
		||||
	/// Valid validators.
 | 
			
		||||
@ -121,11 +123,27 @@ const U16_MAX: usize = ::std::u16::MAX as usize;
 | 
			
		||||
 | 
			
		||||
impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
 | 
			
		||||
	fn from(p: ethjson::spec::AuthorityRoundParams) -> Self {
 | 
			
		||||
		let mut step_duration_usize: usize = p.step_duration.into();
 | 
			
		||||
		if step_duration_usize > U16_MAX {
 | 
			
		||||
			step_duration_usize = U16_MAX;
 | 
			
		||||
			warn!(target: "engine", "step_duration is too high ({}), setting it to {}", step_duration_usize, U16_MAX);
 | 
			
		||||
		}
 | 
			
		||||
		let map_step_duration = |u: ethjson::uint::Uint| {
 | 
			
		||||
			let mut step_duration_usize: usize = u.into();
 | 
			
		||||
			if step_duration_usize == 0 {
 | 
			
		||||
				panic!("AuthorityRoundParams: step duration cannot be 0");
 | 
			
		||||
			}
 | 
			
		||||
			if step_duration_usize > U16_MAX {
 | 
			
		||||
				warn!(target: "engine", "step duration is too high ({}), setting it to {}", step_duration_usize, U16_MAX);
 | 
			
		||||
				step_duration_usize = U16_MAX;
 | 
			
		||||
			}
 | 
			
		||||
			step_duration_usize as u64
 | 
			
		||||
		};
 | 
			
		||||
		let step_durations: BTreeMap<_, _> = match p.step_duration {
 | 
			
		||||
			ethjson::spec::StepDuration::Single(u) =>
 | 
			
		||||
				iter::once((0,  map_step_duration(u))).collect(),
 | 
			
		||||
			ethjson::spec::StepDuration::Transitions(tr) => {
 | 
			
		||||
				if tr.is_empty() {
 | 
			
		||||
					panic!("AuthorityRoundParams: step duration transitions cannot be empty");
 | 
			
		||||
				}
 | 
			
		||||
				tr.into_iter().map(|(timestamp, u)| (timestamp.into(), map_step_duration(u))).collect()
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
		let transition_block_num = p.block_reward_contract_transition.map_or(0, Into::into);
 | 
			
		||||
		let mut br_transitions: BTreeMap<_, _> = p.block_reward_contract_transitions
 | 
			
		||||
			.unwrap_or_default()
 | 
			
		||||
@ -151,7 +169,7 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
		AuthorityRoundParams {
 | 
			
		||||
			step_duration: step_duration_usize as u16,
 | 
			
		||||
			step_durations,
 | 
			
		||||
			validators: new_validator_set(p.validators),
 | 
			
		||||
			start_step: p.start_step.map(Into::into),
 | 
			
		||||
			validate_score_transition: p.validate_score_transition.map_or(0, Into::into),
 | 
			
		||||
@ -169,54 +187,89 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helper for managing the step.
 | 
			
		||||
/// A triple containing the first step number and the starting timestamp of the given step duration.
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
struct StepDurationInfo {
 | 
			
		||||
	transition_step: u64,
 | 
			
		||||
	transition_timestamp: u64,
 | 
			
		||||
	step_duration: u64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Helper for managing the step.
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
struct Step {
 | 
			
		||||
	calibrate: bool, // whether calibration is enabled.
 | 
			
		||||
	inner: AtomicUsize,
 | 
			
		||||
	duration: u16,
 | 
			
		||||
	inner: AtomicU64,
 | 
			
		||||
	/// Planned durations of steps.
 | 
			
		||||
	durations: Vec<StepDurationInfo>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Step {
 | 
			
		||||
	fn load(&self) -> u64 { self.inner.load(AtomicOrdering::SeqCst) as u64 }
 | 
			
		||||
	fn load(&self) -> u64 { self.inner.load(AtomicOrdering::SeqCst) }
 | 
			
		||||
 | 
			
		||||
	/// Finds the remaining duration of the current step. Panics if there was a counter under- or
 | 
			
		||||
	/// overflow.
 | 
			
		||||
	fn duration_remaining(&self) -> Duration {
 | 
			
		||||
		let now = unix_now();
 | 
			
		||||
		let expected_seconds = self.load()
 | 
			
		||||
			.checked_add(1)
 | 
			
		||||
			.and_then(|ctr| ctr.checked_mul(self.duration as u64))
 | 
			
		||||
			.map(Duration::from_secs);
 | 
			
		||||
 | 
			
		||||
		match expected_seconds {
 | 
			
		||||
			Some(step_end) if step_end > now => step_end - now,
 | 
			
		||||
			Some(_) => Duration::from_secs(0),
 | 
			
		||||
			None => {
 | 
			
		||||
				let ctr = self.load();
 | 
			
		||||
				error!(target: "engine", "Step counter is too high: {}, aborting", ctr);
 | 
			
		||||
				panic!("step counter is too high: {}", ctr)
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		self.opt_duration_remaining().unwrap_or_else(|| {
 | 
			
		||||
			let ctr = self.load();
 | 
			
		||||
			error!(target: "engine", "Step counter under- or overflow: {}, aborting", ctr);
 | 
			
		||||
			panic!("step counter under- or overflow: {}", ctr)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Finds the remaining duration of the current step. Returns `None` if there was a counter
 | 
			
		||||
	/// under- or overflow.
 | 
			
		||||
	fn opt_duration_remaining(&self) -> Option<Duration> {
 | 
			
		||||
		let next_step = self.load().checked_add(1)?;
 | 
			
		||||
		let StepDurationInfo { transition_step, transition_timestamp, step_duration } =
 | 
			
		||||
			self.durations.iter()
 | 
			
		||||
			.take_while(|info| info.transition_step < next_step)
 | 
			
		||||
			.last()
 | 
			
		||||
			.expect("durations cannot be empty")
 | 
			
		||||
			.clone();
 | 
			
		||||
		let next_time = transition_timestamp
 | 
			
		||||
			.checked_add(next_step.checked_sub(transition_step)?.checked_mul(step_duration)?)?;
 | 
			
		||||
		Some(Duration::from_secs(next_time.saturating_sub(unix_now().as_secs())))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Increments the step number.
 | 
			
		||||
	///
 | 
			
		||||
	/// Panics if the new step number is `u64::MAX`.
 | 
			
		||||
	fn increment(&self) {
 | 
			
		||||
		use std::usize;
 | 
			
		||||
		// fetch_add won't panic on overflow but will rather wrap
 | 
			
		||||
		// around, leading to zero as the step counter, which might
 | 
			
		||||
		// lead to unexpected situations, so it's better to shut down.
 | 
			
		||||
		if self.inner.fetch_add(1, AtomicOrdering::SeqCst) == usize::MAX {
 | 
			
		||||
			error!(target: "engine", "Step counter is too high: {}, aborting", usize::MAX);
 | 
			
		||||
			panic!("step counter is too high: {}", usize::MAX);
 | 
			
		||||
		if self.inner.fetch_add(1, AtomicOrdering::SeqCst) == u64::MAX {
 | 
			
		||||
			error!(target: "engine", "Step counter is too high: {}, aborting", u64::MAX);
 | 
			
		||||
			panic!("step counter is too high: {}", u64::MAX);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn calibrate(&self) {
 | 
			
		||||
		if self.calibrate {
 | 
			
		||||
			let new_step = unix_now().as_secs() / (self.duration as u64);
 | 
			
		||||
			self.inner.store(new_step as usize, AtomicOrdering::SeqCst);
 | 
			
		||||
			if self.opt_calibrate().is_none() {
 | 
			
		||||
				let ctr = self.load();
 | 
			
		||||
				error!(target: "engine", "Step counter under- or overflow: {}, aborting", ctr);
 | 
			
		||||
				panic!("step counter under- or overflow: {}", ctr)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Calibrates the AuRa step number according to the current time.
 | 
			
		||||
	fn opt_calibrate(&self) -> Option<()> {
 | 
			
		||||
		let now = unix_now().as_secs();
 | 
			
		||||
		let StepDurationInfo { transition_step, transition_timestamp, step_duration } =
 | 
			
		||||
			self.durations.iter()
 | 
			
		||||
			.take_while(|info| info.transition_timestamp < now)
 | 
			
		||||
			.last()
 | 
			
		||||
			.expect("durations cannot be empty")
 | 
			
		||||
			.clone();
 | 
			
		||||
		let new_step = (now.checked_sub(transition_timestamp)? / step_duration)
 | 
			
		||||
			.checked_add(transition_step)?;
 | 
			
		||||
		self.inner.store(new_step, AtomicOrdering::SeqCst);
 | 
			
		||||
		Some(())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn check_future(&self, given: u64) -> Result<(), Option<OutOfBounds<u64>>> {
 | 
			
		||||
		const REJECTED_STEP_DRIFT: u64 = 4;
 | 
			
		||||
 | 
			
		||||
@ -234,7 +287,9 @@ impl Step {
 | 
			
		||||
			Err(None)
 | 
			
		||||
		// wait a bit for blocks in near future
 | 
			
		||||
		} else if given > current {
 | 
			
		||||
			let d = self.duration as u64;
 | 
			
		||||
			let d = self.durations.iter().take_while(|info| info.transition_step <= current).last()
 | 
			
		||||
				.expect("Duration map has at least a 0 entry.")
 | 
			
		||||
				.step_duration;
 | 
			
		||||
			Err(Some(OutOfBounds {
 | 
			
		||||
				min: None,
 | 
			
		||||
				max: Some(d * current),
 | 
			
		||||
@ -730,23 +785,54 @@ impl<'a, A: ?Sized, B> Deref for CowLike<'a, A, B> where B: AsRef<A> {
 | 
			
		||||
impl AuthorityRound {
 | 
			
		||||
	/// Create a new instance of AuthorityRound engine.
 | 
			
		||||
	pub fn new(our_params: AuthorityRoundParams, machine: Machine) -> Result<Arc<Self>, Error> {
 | 
			
		||||
		if our_params.step_duration == 0 {
 | 
			
		||||
			error!(target: "engine", "Authority Round step duration can't be zero, aborting");
 | 
			
		||||
			panic!("authority_round: step duration can't be zero")
 | 
			
		||||
		if !our_params.step_durations.contains_key(&0) {
 | 
			
		||||
			error!(target: "engine", "Authority Round step 0 duration is undefined, aborting");
 | 
			
		||||
			return Err(Error::Engine(EngineError::Custom(String::from("step 0 duration is undefined"))));
 | 
			
		||||
		}
 | 
			
		||||
		if our_params.step_durations.values().any(|v| *v == 0) {
 | 
			
		||||
			error!(target: "engine", "Authority Round step duration cannot be 0");
 | 
			
		||||
			return Err(Error::Engine(EngineError::Custom(String::from("step duration cannot be 0"))));
 | 
			
		||||
		}
 | 
			
		||||
		let should_timeout = our_params.start_step.is_none();
 | 
			
		||||
		let initial_step = our_params.start_step.unwrap_or_else(|| (unix_now().as_secs() / (our_params.step_duration as u64)));
 | 
			
		||||
		let initial_step = our_params.start_step.unwrap_or(0);
 | 
			
		||||
 | 
			
		||||
		let mut durations = Vec::new();
 | 
			
		||||
		let mut prev_step = 0u64;
 | 
			
		||||
		let mut prev_time = 0u64;
 | 
			
		||||
		let mut prev_dur = our_params.step_durations[&0];
 | 
			
		||||
		durations.push(StepDurationInfo {
 | 
			
		||||
			transition_step: prev_step,
 | 
			
		||||
			transition_timestamp: prev_time,
 | 
			
		||||
			step_duration: prev_dur
 | 
			
		||||
		});
 | 
			
		||||
		for (time, dur) in our_params.step_durations.iter().skip(1) {
 | 
			
		||||
			let (step, time) = next_step_time_duration(
 | 
			
		||||
				StepDurationInfo{
 | 
			
		||||
					transition_step: prev_step,
 | 
			
		||||
					transition_timestamp: prev_time,
 | 
			
		||||
					step_duration: prev_dur,
 | 
			
		||||
				}, *time)
 | 
			
		||||
				.ok_or(BlockError::TimestampOverflow)?;
 | 
			
		||||
			durations.push(StepDurationInfo {
 | 
			
		||||
				transition_step: step,
 | 
			
		||||
				transition_timestamp: time,
 | 
			
		||||
				step_duration: *dur
 | 
			
		||||
			});
 | 
			
		||||
			prev_step = step;
 | 
			
		||||
			prev_time = time;
 | 
			
		||||
			prev_dur = *dur;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		let step = Step {
 | 
			
		||||
			inner: AtomicU64::new(initial_step),
 | 
			
		||||
			calibrate: our_params.start_step.is_none(),
 | 
			
		||||
			durations,
 | 
			
		||||
		};
 | 
			
		||||
		step.calibrate();
 | 
			
		||||
		let engine = Arc::new(
 | 
			
		||||
			AuthorityRound {
 | 
			
		||||
				transition_service: IoService::<()>::start()?,
 | 
			
		||||
				step: Arc::new(PermissionedStep {
 | 
			
		||||
					inner: Step {
 | 
			
		||||
						inner: AtomicUsize::new(initial_step as usize),
 | 
			
		||||
						calibrate: our_params.start_step.is_none(),
 | 
			
		||||
						duration: our_params.step_duration,
 | 
			
		||||
					},
 | 
			
		||||
					can_propose: AtomicBool::new(true),
 | 
			
		||||
				}),
 | 
			
		||||
				step: Arc::new(PermissionedStep { inner: step, can_propose: AtomicBool::new(true) }),
 | 
			
		||||
				client: Arc::new(RwLock::new(None)),
 | 
			
		||||
				signer: RwLock::new(None),
 | 
			
		||||
				validators: our_params.validators,
 | 
			
		||||
@ -994,8 +1080,10 @@ impl IoHandler<()> for TransitionHandler {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			let next_run_at = AsMillis::as_millis(&self.step.inner.duration_remaining()) >> 2;
 | 
			
		||||
			io.register_timer_once(ENGINE_TIMEOUT_TOKEN, Duration::from_millis(next_run_at))
 | 
			
		||||
			let next_run_at = Duration::from_millis(
 | 
			
		||||
				AsMillis::as_millis(&self.step.inner.duration_remaining()) >> 2
 | 
			
		||||
			);
 | 
			
		||||
			io.register_timer_once(ENGINE_TIMEOUT_TOKEN, next_run_at)
 | 
			
		||||
				.unwrap_or_else(|e| warn!(target: "engine", "Failed to restart consensus step timer: {}.", e))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -1694,12 +1782,27 @@ impl Engine for AuthorityRound {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A helper accumulator function mapping a step duration and a step duration transition timestamp
 | 
			
		||||
/// to the corresponding step number and the correct starting second of the step.
 | 
			
		||||
fn next_step_time_duration(info: StepDurationInfo, time: u64) -> Option<(u64, u64)>
 | 
			
		||||
{
 | 
			
		||||
	let step_diff = time.checked_add(info.step_duration)?
 | 
			
		||||
		.checked_sub(1)?
 | 
			
		||||
		.checked_sub(info.transition_timestamp)?
 | 
			
		||||
		.checked_div(info.step_duration)?;
 | 
			
		||||
	Some((
 | 
			
		||||
		info.transition_step.checked_add(step_diff)?,
 | 
			
		||||
		step_diff.checked_mul(info.step_duration)?.checked_add(time)?,
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
	use std::collections::BTreeMap;
 | 
			
		||||
	use std::str::FromStr;
 | 
			
		||||
	use std::sync::Arc;
 | 
			
		||||
	use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
 | 
			
		||||
	use std::sync::atomic::{AtomicUsize, AtomicU64, Ordering as AtomicOrdering};
 | 
			
		||||
	use std::time::Duration;
 | 
			
		||||
	use keccak_hash::keccak;
 | 
			
		||||
	use accounts::AccountProvider;
 | 
			
		||||
	use ethereum_types::{Address, H520, H256, U256};
 | 
			
		||||
@ -1726,13 +1829,16 @@ mod tests {
 | 
			
		||||
	use ethjson;
 | 
			
		||||
	use serde_json;
 | 
			
		||||
 | 
			
		||||
	use super::{AuthorityRoundParams, AuthorityRound, EmptyStep, SealedEmptyStep, calculate_score};
 | 
			
		||||
	use super::{
 | 
			
		||||
		AuthorityRoundParams, AuthorityRound, EmptyStep, SealedEmptyStep, StepDurationInfo,
 | 
			
		||||
		calculate_score,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	fn build_aura<F>(f: F) -> Arc<AuthorityRound> where
 | 
			
		||||
		F: FnOnce(&mut AuthorityRoundParams),
 | 
			
		||||
	{
 | 
			
		||||
		let mut params = AuthorityRoundParams {
 | 
			
		||||
			step_duration: 1,
 | 
			
		||||
			step_durations: [(0, 1)].to_vec().into_iter().collect(),
 | 
			
		||||
			start_step: Some(1),
 | 
			
		||||
			validators: Box::new(TestSet::default()),
 | 
			
		||||
			validate_score_transition: 0,
 | 
			
		||||
@ -2061,29 +2167,67 @@ mod tests {
 | 
			
		||||
		use super::Step;
 | 
			
		||||
		let step = Step {
 | 
			
		||||
			calibrate: false,
 | 
			
		||||
			inner: AtomicUsize::new(::std::usize::MAX),
 | 
			
		||||
			duration: 1,
 | 
			
		||||
			inner: AtomicU64::new(::std::u64::MAX),
 | 
			
		||||
			durations: [StepDurationInfo {
 | 
			
		||||
				transition_step: 0,
 | 
			
		||||
				transition_timestamp: 0,
 | 
			
		||||
				step_duration: 1,
 | 
			
		||||
			}].to_vec().into_iter().collect(),
 | 
			
		||||
		};
 | 
			
		||||
		step.increment();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	#[should_panic(expected="counter is too high")]
 | 
			
		||||
	#[should_panic(expected="step counter under- or overflow")]
 | 
			
		||||
	fn test_counter_duration_remaining_too_high() {
 | 
			
		||||
		use super::Step;
 | 
			
		||||
		let step = Step {
 | 
			
		||||
			calibrate: false,
 | 
			
		||||
			inner: AtomicUsize::new(::std::usize::MAX),
 | 
			
		||||
			duration: 1,
 | 
			
		||||
			inner: AtomicU64::new(::std::u64::MAX),
 | 
			
		||||
			durations: [StepDurationInfo {
 | 
			
		||||
				transition_step: 0,
 | 
			
		||||
				transition_timestamp: 0,
 | 
			
		||||
				step_duration: 1,
 | 
			
		||||
			}].to_vec().into_iter().collect(),
 | 
			
		||||
		};
 | 
			
		||||
		step.duration_remaining();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	#[should_panic(expected="authority_round: step duration can't be zero")]
 | 
			
		||||
	fn test_change_step_duration() {
 | 
			
		||||
		use super::Step;
 | 
			
		||||
		use std::thread;
 | 
			
		||||
 | 
			
		||||
		let now = super::unix_now().as_secs();
 | 
			
		||||
		let step = Step {
 | 
			
		||||
			calibrate: true,
 | 
			
		||||
			inner: AtomicU64::new(::std::u64::MAX),
 | 
			
		||||
			durations: [
 | 
			
		||||
				StepDurationInfo { transition_step: 0, transition_timestamp: 0, step_duration: 1 },
 | 
			
		||||
				StepDurationInfo { transition_step: now, transition_timestamp: now, step_duration: 2 },
 | 
			
		||||
				StepDurationInfo { transition_step: now + 1, transition_timestamp: now + 2, step_duration: 4 },
 | 
			
		||||
			].to_vec().into_iter().collect(),
 | 
			
		||||
		};
 | 
			
		||||
		// calibrated step `now`
 | 
			
		||||
		step.calibrate();
 | 
			
		||||
		let duration_remaining = step.duration_remaining();
 | 
			
		||||
		assert_eq!(step.inner.load(AtomicOrdering::SeqCst), now);
 | 
			
		||||
		assert!(duration_remaining <= Duration::from_secs(2));
 | 
			
		||||
		thread::sleep(duration_remaining);
 | 
			
		||||
		step.increment();
 | 
			
		||||
		// calibrated step `now + 1`
 | 
			
		||||
		step.calibrate();
 | 
			
		||||
		let duration_remaining = step.duration_remaining();
 | 
			
		||||
		assert_eq!(step.inner.load(AtomicOrdering::SeqCst), now + 1);
 | 
			
		||||
		assert!(duration_remaining > Duration::from_secs(2));
 | 
			
		||||
		assert!(duration_remaining <= Duration::from_secs(4));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	#[should_panic(expected="called `Result::unwrap()` on an `Err` value: Engine(Custom(\"step duration cannot be 0\"))")]
 | 
			
		||||
	fn test_step_duration_zero() {
 | 
			
		||||
		build_aura(|params| {
 | 
			
		||||
			params.step_duration = 0;
 | 
			
		||||
			params.step_durations = [(0, 0)].to_vec().into_iter().collect();
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -2473,7 +2617,7 @@ mod tests {
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn test_empty_steps() {
 | 
			
		||||
		let engine = build_aura(|p| {
 | 
			
		||||
			p.step_duration = 4;
 | 
			
		||||
			p.step_durations = [(0, 4)].to_vec().into_iter().collect();
 | 
			
		||||
			p.empty_steps_transition = 0;
 | 
			
		||||
			p.maximum_empty_steps = 0;
 | 
			
		||||
		});
 | 
			
		||||
@ -2507,7 +2651,7 @@ mod tests {
 | 
			
		||||
		let (_spec, tap, accounts) = setup_empty_steps();
 | 
			
		||||
		let engine = build_aura(|p| {
 | 
			
		||||
			p.validators = Box::new(SimpleList::new(accounts.clone()));
 | 
			
		||||
			p.step_duration = 4;
 | 
			
		||||
			p.step_durations = [(0, 4)].to_vec().into_iter().collect();
 | 
			
		||||
			p.empty_steps_transition = 0;
 | 
			
		||||
			p.maximum_empty_steps = 0;
 | 
			
		||||
		});
 | 
			
		||||
@ -2544,7 +2688,7 @@ mod tests {
 | 
			
		||||
		let (_spec, tap, accounts) = setup_empty_steps();
 | 
			
		||||
		let engine = build_aura(|p| {
 | 
			
		||||
			p.validators = Box::new(SimpleList::new(accounts.clone()));
 | 
			
		||||
			p.step_duration = 4;
 | 
			
		||||
			p.step_durations = [(0, 4)].to_vec().into_iter().collect();
 | 
			
		||||
			p.empty_steps_transition = 0;
 | 
			
		||||
			p.maximum_empty_steps = 0;
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
@ -41,7 +41,7 @@
 | 
			
		||||
use std::collections::BTreeMap;
 | 
			
		||||
use crate::{bytes::Bytes, hash::Address, uint::Uint};
 | 
			
		||||
use serde::Deserialize;
 | 
			
		||||
use super::ValidatorSet;
 | 
			
		||||
use super::{StepDuration, ValidatorSet};
 | 
			
		||||
 | 
			
		||||
/// Authority params deserialization.
 | 
			
		||||
#[derive(Debug, PartialEq, Deserialize)]
 | 
			
		||||
@ -49,7 +49,7 @@ use super::ValidatorSet;
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct AuthorityRoundParams {
 | 
			
		||||
	/// Block duration, in seconds.
 | 
			
		||||
	pub step_duration: Uint,
 | 
			
		||||
	pub step_duration: StepDuration,
 | 
			
		||||
	/// Valid authorities
 | 
			
		||||
	pub validators: ValidatorSet,
 | 
			
		||||
	/// Starting step. Determined automatically if not specified.
 | 
			
		||||
@ -107,7 +107,7 @@ pub struct AuthorityRound {
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
	use super::{Address, Uint};
 | 
			
		||||
	use super::{Address, Uint, StepDuration};
 | 
			
		||||
	use ethereum_types::{U256, H160};
 | 
			
		||||
	use crate::spec::{validator_set::ValidatorSet, authority_round::AuthorityRound};
 | 
			
		||||
	use std::str::FromStr;
 | 
			
		||||
@ -129,7 +129,7 @@ mod tests {
 | 
			
		||||
		}"#;
 | 
			
		||||
 | 
			
		||||
		let deserialized: AuthorityRound = serde_json::from_str(s).unwrap();
 | 
			
		||||
		assert_eq!(deserialized.params.step_duration, Uint(U256::from(0x02)));
 | 
			
		||||
		assert_eq!(deserialized.params.step_duration, StepDuration::Single(Uint(U256::from(2))));
 | 
			
		||||
		assert_eq!(
 | 
			
		||||
			deserialized.params.validators,
 | 
			
		||||
			ValidatorSet::List(vec![Address(H160::from_str("c6d9d2cd449a754c494264e1809c50e34d64562b").unwrap())]),
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,7 @@ pub mod null_engine;
 | 
			
		||||
pub mod instant_seal;
 | 
			
		||||
pub mod hardcoded_sync;
 | 
			
		||||
pub mod clique;
 | 
			
		||||
pub mod step_duration;
 | 
			
		||||
 | 
			
		||||
pub use self::account::Account;
 | 
			
		||||
pub use self::builtin::{Builtin, Pricing, Linear};
 | 
			
		||||
@ -49,3 +50,4 @@ pub use self::clique::{Clique, CliqueParams};
 | 
			
		||||
pub use self::null_engine::{NullEngine, NullEngineParams};
 | 
			
		||||
pub use self::instant_seal::{InstantSeal, InstantSealParams};
 | 
			
		||||
pub use self::hardcoded_sync::HardcodedSync;
 | 
			
		||||
pub use self::step_duration::StepDuration;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										36
									
								
								json/src/spec/step_duration.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								json/src/spec/step_duration.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
 | 
			
		||||
// This file is part of Parity.
 | 
			
		||||
 | 
			
		||||
// Parity is free software: you can redistribute it and/or modify
 | 
			
		||||
// it under the terms of the GNU General Public License as published by
 | 
			
		||||
// the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
// (at your option) any later version.
 | 
			
		||||
 | 
			
		||||
// Parity is distributed in the hope that it will be useful,
 | 
			
		||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
// GNU General Public License for more details.
 | 
			
		||||
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
//! Step duration configuration parameter
 | 
			
		||||
 | 
			
		||||
use std::collections::BTreeMap;
 | 
			
		||||
 | 
			
		||||
use serde::Deserialize;
 | 
			
		||||
 | 
			
		||||
use crate::uint::Uint;
 | 
			
		||||
 | 
			
		||||
/// Step duration can be specified either as a `Uint` (in seconds), in which case it will be
 | 
			
		||||
/// constant, or as a list of pairs consisting of a timestamp of type `Uint` and a duration, in
 | 
			
		||||
/// which case the duration of a step will be determined by a mapping arising from that list.
 | 
			
		||||
#[derive(Debug, PartialEq, Deserialize)]
 | 
			
		||||
#[serde(deny_unknown_fields)]
 | 
			
		||||
#[serde(untagged)]
 | 
			
		||||
pub enum StepDuration {
 | 
			
		||||
	/// Duration of all steps.
 | 
			
		||||
	Single(Uint),
 | 
			
		||||
	/// Step duration transitions: a mapping of timestamp to step durations.
 | 
			
		||||
	Transitions(BTreeMap<Uint, Uint>),
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user