Backports for beta 2.2.5 (#10047)

* bump beta to 2.2.5

* 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.

* Strict empty steps validation (#10041)

* Add two failings tests for strict empty steps.

* Implement strict validation of empty steps.

* ethcore: enable constantinople on ethereum (#10031)

* ethcore: change blockreward to 2e18 for foundation after constantinople

* ethcore: delay diff bomb by 2e6 blocks for foundation after constantinople

* ethcore: enable eip-{145,1014,1052,1283} for foundation after constantinople

* Change test miner max memory to malloc reports. (#10024)

* Fix: test corpus_inaccessible panic (#10019)

If system uptime is less than the duration in the test, thread
will panic due to: 'overflow when subtracting duration from instant'.

Changed duration to 20 seconds to make it unlikely the test will
fail due to this condition.

Since no operations that carry a similar risk of panic occur outside
of the tests, it doesn't seem warranted to depend on an external crate
to fix this.

* Bump crossbeam. (#10048)
This commit is contained in:
Thibaut Sardan
2018-12-13 17:52:49 +01:00
committed by GitHub
parent f44d885b6d
commit 7fbcdfeed4
13 changed files with 292 additions and 166 deletions

View File

@@ -12,7 +12,7 @@ blooms-db = { path = "../util/blooms-db" }
bn = { git = "https://github.com/paritytech/bn", default-features = false }
byteorder = "1.0"
common-types = { path = "types" }
crossbeam = "0.3"
crossbeam = "0.4"
ethash = { path = "../ethash" }
ethcore-bloom-journal = { path = "../util/bloom" }
parity-bytes = "0.1"

View File

@@ -179,14 +179,15 @@ mod tests {
#[test]
fn corpus_inaccessible() {
let mut cache = Cache::new(Default::default(), Duration::from_secs(5 * 3600));
let duration = Duration::from_secs(20);
let mut cache = Cache::new(Default::default(), duration.clone());
cache.set_gas_price_corpus(vec![].into());
assert_eq!(cache.gas_price_corpus(), Some(vec![].into()));
{
let corpus_time = &mut cache.corpus.as_mut().unwrap().1;
*corpus_time = *corpus_time - Duration::from_secs(6 * 3600);
*corpus_time = *corpus_time - duration;
}
assert!(cache.gas_price_corpus().is_none());
}

View File

@@ -9,7 +9,8 @@
"durationLimit": "0x0d",
"blockReward": {
"0": "0x4563918244F40000",
"4370000": "0x29A2241AF62C0000"
"4370000": "0x29A2241AF62C0000",
"7080000": "0x1BC16D674EC80000"
},
"homesteadTransition": "0x118c30",
"daoHardforkTransition": "0x1d4c00",
@@ -134,7 +135,8 @@
],
"eip100bTransition": 4370000,
"difficultyBombDelays": {
"4370000": 3000000
"4370000": 3000000,
"7080000": 2000000
}
}
}
@@ -159,7 +161,11 @@
"eip140Transition": 4370000,
"eip211Transition": 4370000,
"eip214Transition": 4370000,
"eip658Transition": 4370000
"eip658Transition": 4370000,
"eip145Transition": 7080000,
"eip1014Transition": 7080000,
"eip1052Transition": 7080000,
"eip1283Transition": 7080000
},
"genesis": {
"seal": {

View File

@@ -2305,11 +2305,7 @@ impl ScheduleInfo for Client {
impl ImportSealedBlock for Client {
fn import_sealed_block(&self, block: SealedBlock) -> EthcoreResult<H256> {
let start = Instant::now();
let raw = block.rlp_bytes();
let header = block.header().clone();
let hash = header.hash();
self.notify(|n| n.block_pre_import(&raw, &hash, header.difficulty()));
let route = {
// Do a super duper basic verification to detect potential bugs
if let Err(e) = self.engine.verify_block_basic(&header) {
@@ -2327,14 +2323,15 @@ impl ImportSealedBlock for Client {
let block_data = block.rlp_bytes();
let route = self.importer.commit_block(block, &header, encoded::Block::new(block_data), self);
trace!(target: "client", "Imported sealed block #{} ({})", header.number(), hash);
trace!(target: "client", "Imported sealed block #{} ({})", header.number(), header.hash());
self.state_db.write().sync_cache(&route.enacted, &route.retracted, false);
route
};
let h = header.hash();
let route = ChainRoute::from([route].as_ref());
self.importer.miner.chain_new_blocks(
self,
&[hash],
&[h],
&[],
route.enacted(),
route.retracted(),
@@ -2342,16 +2339,16 @@ impl ImportSealedBlock for Client {
);
self.notify(|notify| {
notify.new_blocks(
vec![hash],
vec![h],
vec![],
route.clone(),
vec![hash],
vec![h],
vec![],
start.elapsed(),
);
});
self.db.read().key_value().flush().expect("DB flush failed.");
Ok(hash)
Ok(h)
}
}

View File

@@ -81,6 +81,8 @@ pub struct AuthorityRoundParams {
pub empty_steps_transition: u64,
/// Number of accepted empty steps.
pub maximum_empty_steps: usize,
/// Transition block to strict empty steps validation.
pub strict_empty_steps_transition: u64,
}
const U16_MAX: usize = ::std::u16::MAX as usize;
@@ -110,6 +112,7 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
maximum_uncle_count: p.maximum_uncle_count.map_or(0, Into::into),
empty_steps_transition: p.empty_steps_transition.map_or(u64::max_value(), |n| ::std::cmp::max(n.into(), 1)),
maximum_empty_steps: p.maximum_empty_steps.map_or(0, Into::into),
strict_empty_steps_transition: p.strict_empty_steps_transition.map_or(0, Into::into),
}
}
}
@@ -421,6 +424,7 @@ pub struct AuthorityRound {
maximum_uncle_count_transition: u64,
maximum_uncle_count: usize,
empty_steps_transition: u64,
strict_empty_steps_transition: u64,
maximum_empty_steps: usize,
machine: EthereumMachine,
}
@@ -674,6 +678,7 @@ impl AuthorityRound {
maximum_uncle_count: our_params.maximum_uncle_count,
empty_steps_transition: our_params.empty_steps_transition,
maximum_empty_steps: our_params.maximum_empty_steps,
strict_empty_steps_transition: our_params.strict_empty_steps_transition,
machine: machine,
});
@@ -1250,8 +1255,11 @@ impl Engine<EthereumMachine> for AuthorityRound {
// reported as there's no way to tell whether the empty step message was never sent or simply not included.
let empty_steps_len = if header.number() >= self.empty_steps_transition {
let validate_empty_steps = || -> Result<usize, Error> {
let strict_empty_steps = header.number() >= self.strict_empty_steps_transition;
let empty_steps = header_empty_steps(header)?;
let empty_steps_len = empty_steps.len();
let mut prev_empty_step = 0;
for empty_step in empty_steps {
if empty_step.step <= parent_step || empty_step.step >= step {
Err(EngineError::InsufficientProof(
@@ -1267,7 +1275,20 @@ impl Engine<EthereumMachine> for AuthorityRound {
Err(EngineError::InsufficientProof(
format!("invalid empty step proof: {:?}", empty_step)))?;
}
if strict_empty_steps {
if empty_step.step <= prev_empty_step {
Err(EngineError::InsufficientProof(format!(
"{} empty step: {:?}",
if empty_step.step == prev_empty_step { "duplicate" } else { "unordered" },
empty_step
)))?;
}
prev_empty_step = empty_step.step;
}
}
Ok(empty_steps_len)
};
@@ -1518,10 +1539,40 @@ mod tests {
use spec::Spec;
use transaction::{Action, Transaction};
use engines::{Seal, Engine, EngineError, EthEngine};
use engines::validator_set::TestSet;
use engines::validator_set::{TestSet, SimpleList};
use error::{Error, ErrorKind};
use super::{AuthorityRoundParams, AuthorityRound, EmptyStep, SealedEmptyStep, calculate_score};
fn aura<F>(f: F) -> Arc<AuthorityRound> where
F: FnOnce(&mut AuthorityRoundParams),
{
let mut params = AuthorityRoundParams {
step_duration: 1,
start_step: Some(1),
validators: Box::new(TestSet::default()),
validate_score_transition: 0,
validate_step_transition: 0,
immediate_transitions: true,
maximum_uncle_count_transition: 0,
maximum_uncle_count: 0,
empty_steps_transition: u64::max_value(),
maximum_empty_steps: 0,
block_reward: Default::default(),
block_reward_contract_transition: 0,
block_reward_contract: Default::default(),
strict_empty_steps_transition: 0,
};
// mutate aura params
f(&mut params);
// create engine
let mut c_params = ::spec::CommonParams::default();
c_params.gas_limit_bound_divisor = 5.into();
let machine = ::machine::EthereumMachine::regular(c_params, Default::default());
AuthorityRound::new(params, machine).unwrap()
}
#[test]
fn has_valid_metadata() {
let engine = Spec::new_test_round().engine;
@@ -1695,28 +1746,9 @@ mod tests {
#[test]
fn reports_skipped() {
let last_benign = Arc::new(AtomicUsize::new(0));
let params = AuthorityRoundParams {
step_duration: 1,
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: u64::max_value(),
maximum_empty_steps: 0,
block_reward: Default::default(),
block_reward_contract_transition: 0,
block_reward_contract: Default::default(),
};
let aura = {
let mut c_params = ::spec::CommonParams::default();
c_params.gas_limit_bound_divisor = 5.into();
let machine = ::machine::EthereumMachine::regular(c_params, Default::default());
AuthorityRound::new(params, machine).unwrap()
};
let aura = aura(|p| {
p.validators = Box::new(TestSet::new(Default::default(), last_benign.clone()));
});
let mut parent_header: Header = Header::default();
parent_header.set_seal(vec![encode(&1usize)]);
@@ -1745,29 +1777,9 @@ mod tests {
#[test]
fn test_uncles_transition() {
let last_benign = Arc::new(AtomicUsize::new(0));
let params = AuthorityRoundParams {
step_duration: 1,
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: 1,
maximum_uncle_count: 0,
empty_steps_transition: u64::max_value(),
maximum_empty_steps: 0,
block_reward: Default::default(),
block_reward_contract_transition: 0,
block_reward_contract: Default::default(),
};
let aura = {
let mut c_params = ::spec::CommonParams::default();
c_params.gas_limit_bound_divisor = 5.into();
let machine = ::machine::EthereumMachine::regular(c_params, Default::default());
AuthorityRound::new(params, machine).unwrap()
};
let aura = aura(|params| {
params.maximum_uncle_count_transition = 1;
});
assert_eq!(aura.maximum_uncle_count(0), 2);
assert_eq!(aura.maximum_uncle_count(1), 0);
@@ -1801,27 +1813,9 @@ mod tests {
#[test]
#[should_panic(expected="authority_round: step duration can't be zero")]
fn test_step_duration_zero() {
let last_benign = Arc::new(AtomicUsize::new(0));
let params = AuthorityRoundParams {
step_duration: 0,
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: u64::max_value(),
maximum_empty_steps: 0,
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());
AuthorityRound::new(params, machine).unwrap();
aura(|params| {
params.step_duration = 0;
});
}
fn setup_empty_steps() -> (Spec, Arc<AccountProvider>, Vec<Address>) {
@@ -1849,6 +1843,23 @@ mod tests {
SealedEmptyStep { signature, step }
}
fn set_empty_steps_seal(header: &mut Header, step: u64, block_signature: &ethkey::Signature, empty_steps: &[SealedEmptyStep]) {
header.set_seal(vec![
encode(&(step as usize)),
encode(&(&**block_signature as &[u8])),
::rlp::encode_list(&empty_steps),
]);
}
fn assert_insufficient_proof<T: ::std::fmt::Debug>(result: Result<T, Error>, contains: &str) {
match result {
Err(Error(ErrorKind::Engine(EngineError::InsufficientProof(ref s)), _)) =>{
assert!(s.contains(contains), "Expected {:?} to contain {:?}", s, contains);
},
e => assert!(false, "Unexpected result: {:?}", e),
}
}
#[test]
fn broadcast_empty_step_message() {
let (spec, tap, accounts) = setup_empty_steps();
@@ -2050,46 +2061,31 @@ mod tests {
// empty step with invalid step
let empty_steps = vec![SealedEmptyStep { signature: 0.into(), step: 2 }];
header.set_seal(vec![
encode(&2usize),
encode(&(&*signature as &[u8])),
::rlp::encode_list(&empty_steps),
]);
set_empty_steps_seal(&mut header, 2, &signature, &empty_steps);
assert!(match engine.verify_block_family(&header, &parent_header) {
Err(Error(ErrorKind::Engine(EngineError::InsufficientProof(ref s)), _))
if s.contains("invalid step") => true,
_ => false,
});
assert_insufficient_proof(
engine.verify_block_family(&header, &parent_header),
"invalid step"
);
// empty step with invalid signature
let empty_steps = vec![SealedEmptyStep { signature: 0.into(), step: 1 }];
header.set_seal(vec![
encode(&2usize),
encode(&(&*signature as &[u8])),
::rlp::encode_list(&empty_steps),
]);
set_empty_steps_seal(&mut header, 2, &signature, &empty_steps);
assert!(match engine.verify_block_family(&header, &parent_header) {
Err(Error(ErrorKind::Engine(EngineError::InsufficientProof(ref s)), _))
if s.contains("invalid empty step proof") => true,
_ => false,
});
assert_insufficient_proof(
engine.verify_block_family(&header, &parent_header),
"invalid empty step proof"
);
// empty step with valid signature from incorrect proposer for step
engine.set_signer(tap.clone(), addr1, "1".into());
let empty_steps = vec![sealed_empty_step(engine, 1, &parent_header.hash())];
header.set_seal(vec![
encode(&2usize),
encode(&(&*signature as &[u8])),
::rlp::encode_list(&empty_steps),
]);
set_empty_steps_seal(&mut header, 2, &signature, &empty_steps);
assert!(match engine.verify_block_family(&header, &parent_header) {
Err(Error(ErrorKind::Engine(EngineError::InsufficientProof(ref s)), _))
if s.contains("invalid empty step proof") => true,
_ => false,
});
assert_insufficient_proof(
engine.verify_block_family(&header, &parent_header),
"invalid empty step proof"
);
// valid empty steps
engine.set_signer(tap.clone(), addr1, "1".into());
@@ -2100,11 +2096,7 @@ mod tests {
let empty_steps = vec![empty_step2, empty_step3];
header.set_difficulty(calculate_score(0, 4, 2));
let signature = tap.sign(addr1, Some("1".into()), header.bare_hash()).unwrap();
header.set_seal(vec![
encode(&4usize),
encode(&(&*signature as &[u8])),
::rlp::encode_list(&empty_steps),
]);
set_empty_steps_seal(&mut header, 4, &signature, &empty_steps);
assert!(engine.verify_block_family(&header, &parent_header).is_ok());
}
@@ -2216,28 +2208,11 @@ mod tests {
#[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 engine = aura(|p| {
p.step_duration = 4;
p.empty_steps_transition = 0;
p.maximum_empty_steps = 0;
});
let parent_hash: H256 = 1.into();
let signature = H520::default();
@@ -2261,4 +2236,85 @@ mod tests {
assert_eq!(engine.empty_steps(0, 3, parent_hash), vec![]);
assert_eq!(engine.empty_steps(0, 4, parent_hash), vec![step(3)]);
}
#[test]
fn should_reject_duplicate_empty_steps() {
// given
let (_spec, tap, accounts) = setup_empty_steps();
let engine = aura(|p| {
p.validators = Box::new(SimpleList::new(accounts.clone()));
p.step_duration = 4;
p.empty_steps_transition = 0;
p.maximum_empty_steps = 0;
});
let mut parent = Header::default();
parent.set_seal(vec![encode(&0usize)]);
let mut header = Header::default();
header.set_number(parent.number() + 1);
header.set_parent_hash(parent.hash());
header.set_author(accounts[0]);
// when
engine.set_signer(tap.clone(), accounts[1], "0".into());
let empty_steps = vec![
sealed_empty_step(&*engine, 1, &parent.hash()),
sealed_empty_step(&*engine, 1, &parent.hash()),
];
let step = 2;
let signature = tap.sign(accounts[0], Some("1".into()), header.bare_hash()).unwrap();
set_empty_steps_seal(&mut header, step, &signature, &empty_steps);
header.set_difficulty(calculate_score(0, step, empty_steps.len()));
// then
assert_insufficient_proof(
engine.verify_block_family(&header, &parent),
"duplicate empty step"
);
}
#[test]
fn should_reject_empty_steps_out_of_order() {
// given
let (_spec, tap, accounts) = setup_empty_steps();
let engine = aura(|p| {
p.validators = Box::new(SimpleList::new(accounts.clone()));
p.step_duration = 4;
p.empty_steps_transition = 0;
p.maximum_empty_steps = 0;
});
let mut parent = Header::default();
parent.set_seal(vec![encode(&0usize)]);
let mut header = Header::default();
header.set_number(parent.number() + 1);
header.set_parent_hash(parent.hash());
header.set_author(accounts[0]);
// when
engine.set_signer(tap.clone(), accounts[1], "0".into());
let es1 = sealed_empty_step(&*engine, 1, &parent.hash());
engine.set_signer(tap.clone(), accounts[0], "1".into());
let es2 = sealed_empty_step(&*engine, 2, &parent.hash());
let mut empty_steps = vec![es2, es1];
let step = 3;
let signature = tap.sign(accounts[1], Some("0".into()), header.bare_hash()).unwrap();
set_empty_steps_seal(&mut header, step, &signature, &empty_steps);
header.set_difficulty(calculate_score(0, step, empty_steps.len()));
// then make sure it's rejected because of the order
assert_insufficient_proof(
engine.verify_block_family(&header, &parent),
"unordered empty step"
);
// now try to fix the order
empty_steps.reverse();
set_empty_steps_seal(&mut header, step, &signature, &empty_steps);
assert_eq!(engine.verify_block_family(&header, &parent).unwrap(), ());
}
}

View File

@@ -34,12 +34,18 @@ pub struct TestSet {
last_benign: Arc<AtomicUsize>,
}
impl Default for TestSet {
fn default() -> Self {
TestSet::new(Default::default(), Default::default())
}
}
impl TestSet {
pub fn new(last_malicious: Arc<AtomicUsize>, last_benign: Arc<AtomicUsize>) -> Self {
TestSet {
validator: SimpleList::new(vec![Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()]),
last_malicious: last_malicious,
last_benign: last_benign,
last_malicious,
last_benign,
}
}
}

View File

@@ -979,7 +979,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
scope.builder().stack_size(::std::cmp::max(self.schedule.max_depth.saturating_sub(depth_threshold) * STACK_SIZE_PER_DEPTH, local_stack_size)).spawn(move || {
self.call_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer)
}).expect("Sub-thread creation cannot fail; the host might run out of resources; qed")
}).join()
}).join().expect("Sub-thread never panics; qed")
}
}
@@ -1063,7 +1063,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
scope.builder().stack_size(::std::cmp::max(self.schedule.max_depth.saturating_sub(depth_threshold) * STACK_SIZE_PER_DEPTH, local_stack_size)).spawn(move || {
self.create_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer)
}).expect("Sub-thread creation cannot fail; the host might run out of resources; qed")
}).join()
}).join().expect("Sub-thread never panics; qed")
}
}

View File

@@ -192,11 +192,11 @@ pub fn take_snapshot<W: SnapshotWriter + Send>(
state_guards.push(state_guard);
}
let block_hashes = block_guard.join()?;
let block_hashes = block_guard.join().expect("Sub-thread never panics; qed")?;
let mut state_hashes = Vec::new();
for guard in state_guards {
let part_state_hashes = guard.join()?;
let part_state_hashes = guard.join().expect("Sub-thread never panics; qed")?;
state_hashes.extend(part_state_hashes);
}