Fix empty steps (#9939)
* Don't send empty step twice or empty step then block. * Perform basic validation of locally sealed blocks. * Don't include empty step twice.
This commit is contained in:
parent
664bb2becd
commit
35a2b87174
@ -2287,26 +2287,34 @@ impl ScheduleInfo for Client {
|
|||||||
|
|
||||||
impl ImportSealedBlock for Client {
|
impl ImportSealedBlock for Client {
|
||||||
fn import_sealed_block(&self, block: SealedBlock) -> EthcoreResult<H256> {
|
fn import_sealed_block(&self, block: SealedBlock) -> EthcoreResult<H256> {
|
||||||
let h = block.header().hash();
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
let header = block.header().clone();
|
||||||
let route = {
|
let route = {
|
||||||
|
// Do a super duper basic verification to detect potential bugs
|
||||||
|
if let Err(e) = self.engine.verify_block_basic(&header) {
|
||||||
|
self.importer.bad_blocks.report(
|
||||||
|
block.rlp_bytes(),
|
||||||
|
format!("Detected an issue with locally sealed block: {}", e),
|
||||||
|
);
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
|
||||||
// scope for self.import_lock
|
// scope for self.import_lock
|
||||||
let _import_lock = self.importer.import_lock.lock();
|
let _import_lock = self.importer.import_lock.lock();
|
||||||
trace_time!("import_sealed_block");
|
trace_time!("import_sealed_block");
|
||||||
|
|
||||||
let number = block.header().number();
|
|
||||||
let block_data = block.rlp_bytes();
|
let block_data = block.rlp_bytes();
|
||||||
let header = block.header().clone();
|
|
||||||
|
|
||||||
let route = self.importer.commit_block(block, &header, encoded::Block::new(block_data), self);
|
let route = self.importer.commit_block(block, &header, encoded::Block::new(block_data), self);
|
||||||
trace!(target: "client", "Imported sealed block #{} ({})", number, h);
|
trace!(target: "client", "Imported sealed block #{} ({})", header.number(), header.hash());
|
||||||
self.state_db.write().sync_cache(&route.enacted, &route.retracted, false);
|
self.state_db.write().sync_cache(&route.enacted, &route.retracted, false);
|
||||||
route
|
route
|
||||||
};
|
};
|
||||||
|
let h = header.hash();
|
||||||
let route = ChainRoute::from([route].as_ref());
|
let route = ChainRoute::from([route].as_ref());
|
||||||
self.importer.miner.chain_new_blocks(
|
self.importer.miner.chain_new_blocks(
|
||||||
self,
|
self,
|
||||||
&[h.clone()],
|
&[h],
|
||||||
&[],
|
&[],
|
||||||
route.enacted(),
|
route.enacted(),
|
||||||
route.retracted(),
|
route.retracted(),
|
||||||
@ -2314,10 +2322,10 @@ impl ImportSealedBlock for Client {
|
|||||||
);
|
);
|
||||||
self.notify(|notify| {
|
self.notify(|notify| {
|
||||||
notify.new_blocks(
|
notify.new_blocks(
|
||||||
vec![h.clone()],
|
vec![h],
|
||||||
vec![],
|
vec![],
|
||||||
route.clone(),
|
route.clone(),
|
||||||
vec![h.clone()],
|
vec![h],
|
||||||
vec![],
|
vec![],
|
||||||
start.elapsed(),
|
start.elapsed(),
|
||||||
);
|
);
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
//! A blockchain engine that supports a non-instant BFT proof-of-authority.
|
//! A blockchain engine that supports a non-instant BFT proof-of-authority.
|
||||||
|
|
||||||
use std::collections::{BTreeMap, HashSet};
|
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
||||||
use std::fmt;
|
use std::{cmp, fmt};
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
|
use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering as AtomicOrdering};
|
||||||
@ -123,10 +123,10 @@ struct Step {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Step {
|
impl Step {
|
||||||
fn load(&self) -> usize { self.inner.load(AtomicOrdering::SeqCst) }
|
fn load(&self) -> u64 { self.inner.load(AtomicOrdering::SeqCst) as u64 }
|
||||||
fn duration_remaining(&self) -> Duration {
|
fn duration_remaining(&self) -> Duration {
|
||||||
let now = unix_now();
|
let now = unix_now();
|
||||||
let expected_seconds = (self.load() as u64)
|
let expected_seconds = self.load()
|
||||||
.checked_add(1)
|
.checked_add(1)
|
||||||
.and_then(|ctr| ctr.checked_mul(self.duration as u64))
|
.and_then(|ctr| ctr.checked_mul(self.duration as u64))
|
||||||
.map(Duration::from_secs);
|
.map(Duration::from_secs);
|
||||||
@ -162,8 +162,8 @@ impl Step {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_future(&self, given: usize) -> Result<(), Option<OutOfBounds<u64>>> {
|
fn check_future(&self, given: u64) -> Result<(), Option<OutOfBounds<u64>>> {
|
||||||
const REJECTED_STEP_DRIFT: usize = 4;
|
const REJECTED_STEP_DRIFT: u64 = 4;
|
||||||
|
|
||||||
// Verify if the step is correct.
|
// Verify if the step is correct.
|
||||||
if given <= self.load() {
|
if given <= self.load() {
|
||||||
@ -182,8 +182,8 @@ impl Step {
|
|||||||
let d = self.duration as u64;
|
let d = self.duration as u64;
|
||||||
Err(Some(OutOfBounds {
|
Err(Some(OutOfBounds {
|
||||||
min: None,
|
min: None,
|
||||||
max: Some(d * current as u64),
|
max: Some(d * current),
|
||||||
found: d * given as u64,
|
found: d * given,
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -192,8 +192,8 @@ impl Step {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Chain scoring: total weight is sqrt(U256::max_value())*height - step
|
// Chain scoring: total weight is sqrt(U256::max_value())*height - step
|
||||||
fn calculate_score(parent_step: U256, current_step: U256, current_empty_steps: U256) -> U256 {
|
fn calculate_score(parent_step: u64, current_step: u64, current_empty_steps: usize) -> U256 {
|
||||||
U256::from(U128::max_value()) + parent_step - current_step + current_empty_steps
|
U256::from(U128::max_value()) + U256::from(parent_step) - U256::from(current_step) + U256::from(current_empty_steps)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EpochManager {
|
struct EpochManager {
|
||||||
@ -284,13 +284,26 @@ impl EpochManager {
|
|||||||
/// A message broadcast by authorities when it's their turn to seal a block but there are no
|
/// A message broadcast by authorities when it's their turn to seal a block but there are no
|
||||||
/// transactions. Other authorities accumulate these messages and later include them in the seal as
|
/// transactions. Other authorities accumulate these messages and later include them in the seal as
|
||||||
/// proof.
|
/// proof.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
struct EmptyStep {
|
struct EmptyStep {
|
||||||
signature: H520,
|
signature: H520,
|
||||||
step: usize,
|
step: u64,
|
||||||
parent_hash: H256,
|
parent_hash: H256,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for EmptyStep {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Ord for EmptyStep {
|
||||||
|
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||||
|
self.step.cmp(&other.step)
|
||||||
|
.then_with(|| self.parent_hash.cmp(&other.parent_hash))
|
||||||
|
.then_with(|| self.signature.cmp(&other.signature))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl EmptyStep {
|
impl EmptyStep {
|
||||||
fn from_sealed(sealed_empty_step: SealedEmptyStep, parent_hash: &H256) -> EmptyStep {
|
fn from_sealed(sealed_empty_step: SealedEmptyStep, parent_hash: &H256) -> EmptyStep {
|
||||||
let signature = sealed_empty_step.signature;
|
let signature = sealed_empty_step.signature;
|
||||||
@ -353,7 +366,7 @@ pub fn empty_step_full_rlp(signature: &H520, empty_step_rlp: &[u8]) -> Vec<u8> {
|
|||||||
s.out()
|
s.out()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn empty_step_rlp(step: usize, parent_hash: &H256) -> Vec<u8> {
|
pub fn empty_step_rlp(step: u64, parent_hash: &H256) -> Vec<u8> {
|
||||||
let mut s = RlpStream::new_list(2);
|
let mut s = RlpStream::new_list(2);
|
||||||
s.append(&step).append(parent_hash);
|
s.append(&step).append(parent_hash);
|
||||||
s.out()
|
s.out()
|
||||||
@ -365,7 +378,7 @@ pub fn empty_step_rlp(step: usize, parent_hash: &H256) -> Vec<u8> {
|
|||||||
/// empty message is included.
|
/// empty message is included.
|
||||||
struct SealedEmptyStep {
|
struct SealedEmptyStep {
|
||||||
signature: H520,
|
signature: H520,
|
||||||
step: usize,
|
step: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encodable for SealedEmptyStep {
|
impl Encodable for SealedEmptyStep {
|
||||||
@ -399,7 +412,7 @@ pub struct AuthorityRound {
|
|||||||
validators: Box<ValidatorSet>,
|
validators: Box<ValidatorSet>,
|
||||||
validate_score_transition: u64,
|
validate_score_transition: u64,
|
||||||
validate_step_transition: u64,
|
validate_step_transition: u64,
|
||||||
empty_steps: Mutex<Vec<EmptyStep>>,
|
empty_steps: Mutex<BTreeSet<EmptyStep>>,
|
||||||
epoch_manager: Mutex<EpochManager>,
|
epoch_manager: Mutex<EpochManager>,
|
||||||
immediate_transitions: bool,
|
immediate_transitions: bool,
|
||||||
block_reward: U256,
|
block_reward: U256,
|
||||||
@ -494,7 +507,7 @@ fn header_expected_seal_fields(header: &Header, empty_steps_transition: u64) ->
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header_step(header: &Header, empty_steps_transition: u64) -> Result<usize, ::rlp::DecoderError> {
|
fn header_step(header: &Header, empty_steps_transition: u64) -> Result<u64, ::rlp::DecoderError> {
|
||||||
let expected_seal_fields = header_expected_seal_fields(header, empty_steps_transition);
|
let expected_seal_fields = header_expected_seal_fields(header, empty_steps_transition);
|
||||||
Rlp::new(&header.seal().get(0).expect(
|
Rlp::new(&header.seal().get(0).expect(
|
||||||
&format!("was either checked with verify_block_basic or is genesis; has {} fields; qed (Make sure the spec file has a correct genesis seal)", expected_seal_fields))).as_val()
|
&format!("was either checked with verify_block_basic or is genesis; has {} fields; qed (Make sure the spec file has a correct genesis seal)", expected_seal_fields))).as_val()
|
||||||
@ -533,17 +546,17 @@ fn header_empty_steps_signers(header: &Header, empty_steps_transition: u64) -> R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step_proposer(validators: &ValidatorSet, bh: &H256, step: usize) -> Address {
|
fn step_proposer(validators: &ValidatorSet, bh: &H256, step: u64) -> Address {
|
||||||
let proposer = validators.get(bh, step);
|
let proposer = validators.get(bh, step as usize);
|
||||||
trace!(target: "engine", "Fetched proposer for step {}: {}", step, proposer);
|
trace!(target: "engine", "Fetched proposer for step {}: {}", step, proposer);
|
||||||
proposer
|
proposer
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_step_proposer(validators: &ValidatorSet, bh: &H256, step: usize, address: &Address) -> bool {
|
fn is_step_proposer(validators: &ValidatorSet, bh: &H256, step: u64, address: &Address) -> bool {
|
||||||
step_proposer(validators, bh, step) == *address
|
step_proposer(validators, bh, step) == *address
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_timestamp(step: &Step, header_step: usize) -> Result<(), BlockError> {
|
fn verify_timestamp(step: &Step, header_step: u64) -> Result<(), BlockError> {
|
||||||
match step.check_future(header_step) {
|
match step.check_future(header_step) {
|
||||||
Err(None) => {
|
Err(None) => {
|
||||||
trace!(target: "engine", "verify_timestamp: block from the future");
|
trace!(target: "engine", "verify_timestamp: block from the future");
|
||||||
@ -564,7 +577,7 @@ fn verify_external(header: &Header, validators: &ValidatorSet, empty_steps_trans
|
|||||||
let header_step = header_step(header, empty_steps_transition)?;
|
let header_step = header_step(header, empty_steps_transition)?;
|
||||||
|
|
||||||
let proposer_signature = header_signature(header, empty_steps_transition)?;
|
let proposer_signature = header_signature(header, empty_steps_transition)?;
|
||||||
let correct_proposer = validators.get(header.parent_hash(), header_step);
|
let correct_proposer = validators.get(header.parent_hash(), header_step as usize);
|
||||||
let is_invalid_proposer = *header.author() != correct_proposer || {
|
let is_invalid_proposer = *header.author() != correct_proposer || {
|
||||||
let empty_steps_rlp = if header.number() >= empty_steps_transition {
|
let empty_steps_rlp = if header.number() >= empty_steps_transition {
|
||||||
Some(header_empty_steps_raw(header))
|
Some(header_empty_steps_raw(header))
|
||||||
@ -634,13 +647,13 @@ impl AuthorityRound {
|
|||||||
panic!("authority_round: step duration can't be zero")
|
panic!("authority_round: step duration can't be zero")
|
||||||
}
|
}
|
||||||
let should_timeout = our_params.start_step.is_none();
|
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))) as usize;
|
let initial_step = our_params.start_step.unwrap_or_else(|| (unix_now().as_secs() / (our_params.step_duration as u64)));
|
||||||
let engine = Arc::new(
|
let engine = Arc::new(
|
||||||
AuthorityRound {
|
AuthorityRound {
|
||||||
transition_service: IoService::<()>::start()?,
|
transition_service: IoService::<()>::start()?,
|
||||||
step: Arc::new(PermissionedStep {
|
step: Arc::new(PermissionedStep {
|
||||||
inner: Step {
|
inner: Step {
|
||||||
inner: AtomicUsize::new(initial_step),
|
inner: AtomicUsize::new(initial_step as usize),
|
||||||
calibrate: our_params.start_step.is_none(),
|
calibrate: our_params.start_step.is_none(),
|
||||||
duration: our_params.step_duration,
|
duration: our_params.step_duration,
|
||||||
},
|
},
|
||||||
@ -651,7 +664,7 @@ impl AuthorityRound {
|
|||||||
validators: our_params.validators,
|
validators: our_params.validators,
|
||||||
validate_score_transition: our_params.validate_score_transition,
|
validate_score_transition: our_params.validate_score_transition,
|
||||||
validate_step_transition: our_params.validate_step_transition,
|
validate_step_transition: our_params.validate_step_transition,
|
||||||
empty_steps: Mutex::new(Vec::new()),
|
empty_steps: Default::default(),
|
||||||
epoch_manager: Mutex::new(EpochManager::blank()),
|
epoch_manager: Mutex::new(EpochManager::blank()),
|
||||||
immediate_transitions: our_params.immediate_transitions,
|
immediate_transitions: our_params.immediate_transitions,
|
||||||
block_reward: our_params.block_reward,
|
block_reward: our_params.block_reward,
|
||||||
@ -699,22 +712,41 @@ impl AuthorityRound {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty_steps(&self, from_step: U256, to_step: U256, parent_hash: H256) -> Vec<EmptyStep> {
|
fn empty_steps(&self, from_step: u64, to_step: u64, parent_hash: H256) -> Vec<EmptyStep> {
|
||||||
self.empty_steps.lock().iter().filter(|e| {
|
let from = EmptyStep {
|
||||||
U256::from(e.step) > from_step &&
|
step: from_step + 1,
|
||||||
U256::from(e.step) < to_step &&
|
parent_hash,
|
||||||
e.parent_hash == parent_hash
|
signature: Default::default(),
|
||||||
}).cloned().collect()
|
};
|
||||||
|
let to = EmptyStep {
|
||||||
|
step: to_step,
|
||||||
|
parent_hash: Default::default(),
|
||||||
|
signature: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if from >= to {
|
||||||
|
return vec![];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_empty_steps(&self, step: U256) {
|
self.empty_steps.lock()
|
||||||
|
.range(from..to)
|
||||||
|
.filter(|e| e.parent_hash == parent_hash)
|
||||||
|
.cloned()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_empty_steps(&self, step: u64) {
|
||||||
// clear old `empty_steps` messages
|
// clear old `empty_steps` messages
|
||||||
self.empty_steps.lock().retain(|e| U256::from(e.step) > step);
|
let mut empty_steps = self.empty_steps.lock();
|
||||||
|
*empty_steps = empty_steps.split_off(&EmptyStep {
|
||||||
|
step: step + 1,
|
||||||
|
parent_hash: Default::default(),
|
||||||
|
signature: Default::default(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_empty_step_message(&self, empty_step: EmptyStep) {
|
fn handle_empty_step_message(&self, empty_step: EmptyStep) {
|
||||||
let mut empty_steps = self.empty_steps.lock();
|
self.empty_steps.lock().insert(empty_step);
|
||||||
empty_steps.push(empty_step);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_empty_step(&self, parent_hash: &H256) {
|
fn generate_empty_step(&self, parent_hash: &H256) {
|
||||||
@ -744,7 +776,7 @@ impl AuthorityRound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_skipped(&self, header: &Header, current_step: usize, parent_step: usize, validators: &ValidatorSet, set_number: u64) {
|
fn report_skipped(&self, header: &Header, current_step: u64, parent_step: u64, validators: &ValidatorSet, set_number: u64) {
|
||||||
// we're building on top of the genesis block so don't report any skipped steps
|
// we're building on top of the genesis block so don't report any skipped steps
|
||||||
if header.number() == 1 {
|
if header.number() == 1 {
|
||||||
return;
|
return;
|
||||||
@ -937,12 +969,12 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
|||||||
let current_step = self.step.inner.load();
|
let current_step = self.step.inner.load();
|
||||||
|
|
||||||
let current_empty_steps_len = if header.number() >= self.empty_steps_transition {
|
let current_empty_steps_len = if header.number() >= self.empty_steps_transition {
|
||||||
self.empty_steps(parent_step.into(), current_step.into(), parent.hash()).len()
|
self.empty_steps(parent_step, current_step, parent.hash()).len()
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
let score = calculate_score(parent_step.into(), current_step.into(), current_empty_steps_len.into());
|
let score = calculate_score(parent_step, current_step, current_empty_steps_len);
|
||||||
header.set_difficulty(score);
|
header.set_difficulty(score);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -986,8 +1018,8 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let header = block.header();
|
let header = block.header();
|
||||||
let parent_step: U256 = header_step(parent, self.empty_steps_transition)
|
let parent_step = header_step(parent, self.empty_steps_transition)
|
||||||
.expect("Header has been verified; qed").into();
|
.expect("Header has been verified; qed");
|
||||||
|
|
||||||
let step = self.step.inner.load();
|
let step = self.step.inner.load();
|
||||||
|
|
||||||
@ -1022,7 +1054,7 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
|||||||
if is_step_proposer(&*validators, header.parent_hash(), step, header.author()) {
|
if is_step_proposer(&*validators, header.parent_hash(), step, header.author()) {
|
||||||
// this is guarded against by `can_propose` unless the block was signed
|
// this is guarded against by `can_propose` unless the block was signed
|
||||||
// on the same step (implies same key) and on a different node.
|
// on the same step (implies same key) and on a different node.
|
||||||
if parent_step == step.into() {
|
if parent_step == step {
|
||||||
warn!("Attempted to seal block on the same step as parent. Is this authority sealing with more than one node?");
|
warn!("Attempted to seal block on the same step as parent. Is this authority sealing with more than one node?");
|
||||||
return Seal::None;
|
return Seal::None;
|
||||||
}
|
}
|
||||||
@ -1034,7 +1066,10 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
|||||||
block.transactions().is_empty() &&
|
block.transactions().is_empty() &&
|
||||||
empty_steps.len() < self.maximum_empty_steps {
|
empty_steps.len() < self.maximum_empty_steps {
|
||||||
|
|
||||||
|
if self.step.can_propose.compare_and_swap(true, false, AtomicOrdering::SeqCst) {
|
||||||
self.generate_empty_step(header.parent_hash());
|
self.generate_empty_step(header.parent_hash());
|
||||||
|
}
|
||||||
|
|
||||||
return Seal::None;
|
return Seal::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1058,7 +1093,7 @@ impl Engine<EthereumMachine> for AuthorityRound {
|
|||||||
// report any skipped primaries between the parent block and
|
// report any skipped primaries between the parent block and
|
||||||
// the block we're sealing, unless we have empty steps enabled
|
// the block we're sealing, unless we have empty steps enabled
|
||||||
if header.number() < self.empty_steps_transition {
|
if header.number() < self.empty_steps_transition {
|
||||||
self.report_skipped(header, step, u64::from(parent_step) as usize, &*validators, set_number);
|
self.report_skipped(header, step, parent_step, &*validators, set_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fields = vec![
|
let mut fields = vec![
|
||||||
@ -1593,12 +1628,12 @@ mod tests {
|
|||||||
|
|
||||||
// Two validators.
|
// Two validators.
|
||||||
// Spec starts with step 2.
|
// Spec starts with step 2.
|
||||||
header.set_difficulty(calculate_score(U256::from(0), U256::from(2), U256::zero()));
|
header.set_difficulty(calculate_score(0, 2, 0));
|
||||||
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
||||||
header.set_seal(vec![encode(&2usize), encode(&(&*signature as &[u8]))]);
|
header.set_seal(vec![encode(&2usize), encode(&(&*signature as &[u8]))]);
|
||||||
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
||||||
assert!(engine.verify_block_external(&header).is_err());
|
assert!(engine.verify_block_external(&header).is_err());
|
||||||
header.set_difficulty(calculate_score(U256::from(0), U256::from(1), U256::zero()));
|
header.set_difficulty(calculate_score(0, 1, 0));
|
||||||
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
||||||
header.set_seal(vec![encode(&1usize), encode(&(&*signature as &[u8]))]);
|
header.set_seal(vec![encode(&1usize), encode(&(&*signature as &[u8]))]);
|
||||||
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
||||||
@ -1622,7 +1657,7 @@ mod tests {
|
|||||||
|
|
||||||
// Two validators.
|
// Two validators.
|
||||||
// Spec starts with step 2.
|
// Spec starts with step 2.
|
||||||
header.set_difficulty(calculate_score(U256::from(0), U256::from(1), U256::zero()));
|
header.set_difficulty(calculate_score(0, 1, 0));
|
||||||
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
||||||
header.set_seal(vec![encode(&1usize), encode(&(&*signature as &[u8]))]);
|
header.set_seal(vec![encode(&1usize), encode(&(&*signature as &[u8]))]);
|
||||||
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
||||||
@ -1650,10 +1685,10 @@ mod tests {
|
|||||||
// Two validators.
|
// Two validators.
|
||||||
// Spec starts with step 2.
|
// Spec starts with step 2.
|
||||||
header.set_seal(vec![encode(&5usize), encode(&(&*signature as &[u8]))]);
|
header.set_seal(vec![encode(&5usize), encode(&(&*signature as &[u8]))]);
|
||||||
header.set_difficulty(calculate_score(U256::from(4), U256::from(5), U256::zero()));
|
header.set_difficulty(calculate_score(4, 5, 0));
|
||||||
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
|
||||||
header.set_seal(vec![encode(&3usize), encode(&(&*signature as &[u8]))]);
|
header.set_seal(vec![encode(&3usize), encode(&(&*signature as &[u8]))]);
|
||||||
header.set_difficulty(calculate_score(U256::from(4), U256::from(3), U256::zero()));
|
header.set_difficulty(calculate_score(4, 3, 0));
|
||||||
assert!(engine.verify_block_family(&header, &parent_header).is_err());
|
assert!(engine.verify_block_family(&header, &parent_header).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1687,7 +1722,7 @@ mod tests {
|
|||||||
parent_header.set_seal(vec![encode(&1usize)]);
|
parent_header.set_seal(vec![encode(&1usize)]);
|
||||||
parent_header.set_gas_limit("222222".parse::<U256>().unwrap());
|
parent_header.set_gas_limit("222222".parse::<U256>().unwrap());
|
||||||
let mut header: Header = Header::default();
|
let mut header: Header = Header::default();
|
||||||
header.set_difficulty(calculate_score(U256::from(1), U256::from(3), U256::zero()));
|
header.set_difficulty(calculate_score(1, 3, 0));
|
||||||
header.set_gas_limit("222222".parse::<U256>().unwrap());
|
header.set_gas_limit("222222".parse::<U256>().unwrap());
|
||||||
header.set_seal(vec![encode(&3usize)]);
|
header.set_seal(vec![encode(&3usize)]);
|
||||||
|
|
||||||
@ -1801,14 +1836,14 @@ mod tests {
|
|||||||
(spec, tap, accounts)
|
(spec, tap, accounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn empty_step(engine: &EthEngine, step: usize, parent_hash: &H256) -> EmptyStep {
|
fn empty_step(engine: &EthEngine, step: u64, parent_hash: &H256) -> EmptyStep {
|
||||||
let empty_step_rlp = super::empty_step_rlp(step, parent_hash);
|
let empty_step_rlp = super::empty_step_rlp(step, parent_hash);
|
||||||
let signature = engine.sign(keccak(&empty_step_rlp)).unwrap().into();
|
let signature = engine.sign(keccak(&empty_step_rlp)).unwrap().into();
|
||||||
let parent_hash = parent_hash.clone();
|
let parent_hash = parent_hash.clone();
|
||||||
EmptyStep { step, signature, parent_hash }
|
EmptyStep { step, signature, parent_hash }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sealed_empty_step(engine: &EthEngine, step: usize, parent_hash: &H256) -> SealedEmptyStep {
|
fn sealed_empty_step(engine: &EthEngine, step: u64, parent_hash: &H256) -> SealedEmptyStep {
|
||||||
let empty_step_rlp = super::empty_step_rlp(step, parent_hash);
|
let empty_step_rlp = super::empty_step_rlp(step, parent_hash);
|
||||||
let signature = engine.sign(keccak(&empty_step_rlp)).unwrap().into();
|
let signature = engine.sign(keccak(&empty_step_rlp)).unwrap().into();
|
||||||
SealedEmptyStep { signature, step }
|
SealedEmptyStep { signature, step }
|
||||||
@ -1844,6 +1879,11 @@ mod tests {
|
|||||||
|
|
||||||
// we've received the message
|
// we've received the message
|
||||||
assert!(notify.messages.read().contains(&empty_step_rlp));
|
assert!(notify.messages.read().contains(&empty_step_rlp));
|
||||||
|
let len = notify.messages.read().len();
|
||||||
|
|
||||||
|
// make sure that we don't generate empty step for the second time
|
||||||
|
assert_eq!(engine.generate_seal(b1.block(), &genesis_header), Seal::None);
|
||||||
|
assert_eq!(len, notify.messages.read().len());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -2058,7 +2098,7 @@ mod tests {
|
|||||||
let empty_step3 = sealed_empty_step(engine, 3, &parent_header.hash());
|
let empty_step3 = sealed_empty_step(engine, 3, &parent_header.hash());
|
||||||
|
|
||||||
let empty_steps = vec![empty_step2, empty_step3];
|
let empty_steps = vec![empty_step2, empty_step3];
|
||||||
header.set_difficulty(calculate_score(U256::from(0), U256::from(4), U256::from(2)));
|
header.set_difficulty(calculate_score(0, 4, 2));
|
||||||
let signature = tap.sign(addr1, Some("1".into()), header.bare_hash()).unwrap();
|
let signature = tap.sign(addr1, Some("1".into()), header.bare_hash()).unwrap();
|
||||||
header.set_seal(vec![
|
header.set_seal(vec![
|
||||||
encode(&4usize),
|
encode(&4usize),
|
||||||
@ -2173,4 +2213,52 @@ mod tests {
|
|||||||
BTreeMap::default(),
|
BTreeMap::default(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty_steps() {
|
||||||
|
let last_benign = Arc::new(AtomicUsize::new(0));
|
||||||
|
let params = AuthorityRoundParams {
|
||||||
|
step_duration: 4,
|
||||||
|
start_step: Some(1),
|
||||||
|
validators: Box::new(TestSet::new(Default::default(), last_benign.clone())),
|
||||||
|
validate_score_transition: 0,
|
||||||
|
validate_step_transition: 0,
|
||||||
|
immediate_transitions: true,
|
||||||
|
maximum_uncle_count_transition: 0,
|
||||||
|
maximum_uncle_count: 0,
|
||||||
|
empty_steps_transition: 0,
|
||||||
|
maximum_empty_steps: 10,
|
||||||
|
block_reward: Default::default(),
|
||||||
|
block_reward_contract_transition: 0,
|
||||||
|
block_reward_contract: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut c_params = ::spec::CommonParams::default();
|
||||||
|
c_params.gas_limit_bound_divisor = 5.into();
|
||||||
|
let machine = ::machine::EthereumMachine::regular(c_params, Default::default());
|
||||||
|
let engine = AuthorityRound::new(params, machine).unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
let parent_hash: H256 = 1.into();
|
||||||
|
let signature = H520::default();
|
||||||
|
let step = |step: u64| EmptyStep {
|
||||||
|
step,
|
||||||
|
parent_hash,
|
||||||
|
signature,
|
||||||
|
};
|
||||||
|
|
||||||
|
engine.handle_empty_step_message(step(1));
|
||||||
|
engine.handle_empty_step_message(step(3));
|
||||||
|
engine.handle_empty_step_message(step(2));
|
||||||
|
engine.handle_empty_step_message(step(1));
|
||||||
|
|
||||||
|
assert_eq!(engine.empty_steps(0, 4, parent_hash), vec![step(1), step(2), step(3)]);
|
||||||
|
assert_eq!(engine.empty_steps(2, 3, parent_hash), vec![]);
|
||||||
|
assert_eq!(engine.empty_steps(2, 4, parent_hash), vec![step(3)]);
|
||||||
|
|
||||||
|
engine.clear_empty_steps(2);
|
||||||
|
|
||||||
|
assert_eq!(engine.empty_steps(0, 3, parent_hash), vec![]);
|
||||||
|
assert_eq!(engine.empty_steps(0, 4, parent_hash), vec![step(3)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user