From 7fbcdfeed47e544d626fa451cdc8658ccf637859 Mon Sep 17 00:00:00 2001 From: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com> Date: Thu, 13 Dec 2018 17:52:49 +0100 Subject: [PATCH] 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) --- Cargo.lock | 70 ++++- Cargo.toml | 2 +- ethcore/Cargo.toml | 2 +- ethcore/light/src/cache.rs | 5 +- ethcore/res/ethereum/foundation.json | 12 +- ethcore/src/client/client.rs | 15 +- ethcore/src/engines/authority_round/mod.rs | 304 ++++++++++++--------- ethcore/src/engines/validator_set/test.rs | 10 +- ethcore/src/executive.rs | 4 +- ethcore/src/snapshot/mod.rs | 4 +- json/src/spec/authority_round.rs | 2 + miner/src/pool/tests/mod.rs | 26 +- util/version/Cargo.toml | 2 +- 13 files changed, 292 insertions(+), 166 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3765ac5c4..1a5e3f529 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -286,8 +286,26 @@ dependencies = [ [[package]] name = "crossbeam" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-channel 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-deque 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-channel" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "crossbeam-deque" @@ -298,6 +316,15 @@ dependencies = [ "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-deque" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-deque" version = "0.6.1" @@ -334,6 +361,19 @@ dependencies = [ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossbeam-epoch" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crossbeam-utils" version = "0.2.2" @@ -347,6 +387,14 @@ name = "crossbeam-utils" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "crossbeam-utils" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crunchy" version = "0.1.6" @@ -566,7 +614,7 @@ dependencies = [ "bn 0.4.4 (git+https://github.com/paritytech/bn)", "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "common-types 0.1.0", - "crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2162,7 +2210,7 @@ version = "1.12.0" dependencies = [ "jni 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "panic_hook 0.1.0", - "parity-ethereum 2.2.4", + "parity-ethereum 2.2.5", ] [[package]] @@ -2178,7 +2226,7 @@ dependencies = [ [[package]] name = "parity-ethereum" -version = "2.2.4" +version = "2.2.5" dependencies = [ "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2227,7 +2275,7 @@ dependencies = [ "parity-rpc-client 1.4.0", "parity-runtime 0.1.0", "parity-updater 1.12.0", - "parity-version 2.2.4", + "parity-version 2.2.5", "parity-whisper 0.1.0", "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2382,7 +2430,7 @@ dependencies = [ "parity-crypto 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-runtime 0.1.0", "parity-updater 1.12.0", - "parity-version 2.2.4", + "parity-version 2.2.5", "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "patricia-trie 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2479,7 +2527,7 @@ dependencies = [ "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-hash-fetch 1.12.0", "parity-path 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-version 2.2.4", + "parity-version 2.2.5", "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2489,7 +2537,7 @@ dependencies = [ [[package]] name = "parity-version" -version = "2.2.4" +version = "2.2.5" dependencies = [ "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4067,13 +4115,17 @@ dependencies = [ "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum cmake 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "6ec65ee4f9c9d16f335091d23693457ed4928657ba4982289d7fafee03bc614a" "checksum combine 3.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fc1d011beeed29187b8db2ac3925c8dd4d3e87db463dc9d2d2833985388fc5bc" -"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19" +"checksum crossbeam 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d7408247b1b87f480890f28b670c5f8d9a8a4274833433fe74dc0dfd46d33650" +"checksum crossbeam-channel 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7b85741761b7f160bc5e7e0c14986ef685b7f8bf9b7ad081c60c604bb4649827" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" +"checksum crossbeam-deque 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7792c4a9b5a4222f654e3728a3dd945aacc24d2c3a1a096ed265d80e4929cb9a" "checksum crossbeam-deque 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3486aefc4c0487b9cb52372c97df0a48b8c249514af1ee99703bf70d2f2ceda1" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" "checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9" +"checksum crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2449aaa4ec7ef96e5fb24db16024b935df718e9ae1cec0a1e68feeca2efca7b8" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" +"checksum crossbeam-utils 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e07fc155212827475223f0bcfae57e945e694fc90950ddf3f6695bbfd5555c72" "checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" "checksum crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c240f247c278fa08a6d4820a6a222bfc6e0d999e51ba67be94f44c905b2161f2" "checksum ct-logs 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "95a4bf5107667e12bf6ce31a3a5066d67acc88942b6742117a41198734aaccaa" diff --git a/Cargo.toml b/Cargo.toml index 75a5c3008..b70e6a85d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ description = "Parity Ethereum client" name = "parity-ethereum" # NOTE Make sure to update util/version/Cargo.toml as well -version = "2.2.4" +version = "2.2.5" license = "GPL-3.0" authors = ["Parity Technologies "] diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 989c705d1..c9c878845 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -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" diff --git a/ethcore/light/src/cache.rs b/ethcore/light/src/cache.rs index 5f1f546eb..d720626ab 100644 --- a/ethcore/light/src/cache.rs +++ b/ethcore/light/src/cache.rs @@ -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()); } diff --git a/ethcore/res/ethereum/foundation.json b/ethcore/res/ethereum/foundation.json index e14d7e086..ddf8d393d 100644 --- a/ethcore/res/ethereum/foundation.json +++ b/ethcore/res/ethereum/foundation.json @@ -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": { diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 00dc9cab5..2ed1087f7 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -2305,11 +2305,7 @@ impl ScheduleInfo for Client { impl ImportSealedBlock for Client { fn import_sealed_block(&self, block: SealedBlock) -> EthcoreResult { 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) } } diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index bf404b476..6cf7f4091 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -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 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 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 { + 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 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) -> Arc 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, Vec
) { @@ -1849,6 +1843,23 @@ mod tests { SealedEmptyStep { signature, step } } + fn set_empty_steps_seal(header: &mut Header, step: u64, block_signature: ðkey::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(result: Result, 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(), ()); + } } diff --git a/ethcore/src/engines/validator_set/test.rs b/ethcore/src/engines/validator_set/test.rs index 6459803d1..9650526e5 100644 --- a/ethcore/src/engines/validator_set/test.rs +++ b/ethcore/src/engines/validator_set/test.rs @@ -34,12 +34,18 @@ pub struct TestSet { last_benign: Arc, } +impl Default for TestSet { + fn default() -> Self { + TestSet::new(Default::default(), Default::default()) + } +} + impl TestSet { pub fn new(last_malicious: Arc, last_benign: Arc) -> Self { TestSet { validator: SimpleList::new(vec![Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()]), - last_malicious: last_malicious, - last_benign: last_benign, + last_malicious, + last_benign, } } } diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index 9ad15c1aa..bfc4e46fa 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -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") } } diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index bd7e3cb51..d52bf55cd 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -192,11 +192,11 @@ pub fn take_snapshot( 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); } diff --git a/json/src/spec/authority_round.rs b/json/src/spec/authority_round.rs index 17f70dcdd..3afd8855e 100644 --- a/json/src/spec/authority_round.rs +++ b/json/src/spec/authority_round.rs @@ -68,6 +68,8 @@ pub struct AuthorityRoundParams { /// Maximum number of accepted empty steps. #[serde(rename="maximumEmptySteps")] pub maximum_empty_steps: Option, + /// Strict validation of empty steps transition block. + pub strict_empty_steps_transition: Option, } /// Authority engine deserialization. diff --git a/miner/src/pool/tests/mod.rs b/miner/src/pool/tests/mod.rs index 30a3e5130..2fc68d0ac 100644 --- a/miner/src/pool/tests/mod.rs +++ b/miner/src/pool/tests/mod.rs @@ -26,12 +26,18 @@ pub mod client; use self::tx::{Tx, TxExt, PairExt}; use self::client::TestClient; +// max mem for 3 transaction, this is relative +// to the global use allocator, the value is currently +// set to reflect malloc usage. +// 50 was enough when using jmalloc. +const TEST_QUEUE_MAX_MEM: usize = 80; + fn new_queue() -> TransactionQueue { TransactionQueue::new( txpool::Options { max_count: 3, max_per_sender: 3, - max_mem_usage: 50 + max_mem_usage: TEST_QUEUE_MAX_MEM }, verifier::Options { minimal_gas_price: 1.into(), @@ -49,7 +55,7 @@ fn should_return_correct_nonces_when_dropped_because_of_limit() { txpool::Options { max_count: 3, max_per_sender: 1, - max_mem_usage: 50 + max_mem_usage: TEST_QUEUE_MAX_MEM }, verifier::Options { minimal_gas_price: 1.into(), @@ -103,7 +109,7 @@ fn should_never_drop_local_transactions_from_different_senders() { txpool::Options { max_count: 3, max_per_sender: 1, - max_mem_usage: 50 + max_mem_usage: TEST_QUEUE_MAX_MEM }, verifier::Options { minimal_gas_price: 1.into(), @@ -477,7 +483,7 @@ fn should_prefer_current_transactions_when_hitting_the_limit() { txpool::Options { max_count: 1, max_per_sender: 2, - max_mem_usage: 50 + max_mem_usage: TEST_QUEUE_MAX_MEM }, verifier::Options { minimal_gas_price: 1.into(), @@ -707,7 +713,7 @@ fn should_accept_local_transactions_below_min_gas_price() { txpool::Options { max_count: 3, max_per_sender: 3, - max_mem_usage: 50 + max_mem_usage: TEST_QUEUE_MAX_MEM }, verifier::Options { minimal_gas_price: 10.into(), @@ -890,7 +896,7 @@ fn should_include_local_transaction_to_a_full_pool() { txpool::Options { max_count: 1, max_per_sender: 2, - max_mem_usage: 50 + max_mem_usage: TEST_QUEUE_MAX_MEM }, verifier::Options { minimal_gas_price: 1.into(), @@ -922,7 +928,7 @@ fn should_avoid_verifying_transaction_already_in_pool() { txpool::Options { max_count: 1, max_per_sender: 2, - max_mem_usage: 50 + max_mem_usage: TEST_QUEUE_MAX_MEM }, verifier::Options { minimal_gas_price: 1.into(), @@ -957,7 +963,7 @@ fn should_avoid_reverifying_recently_rejected_transactions() { txpool::Options { max_count: 1, max_per_sender: 2, - max_mem_usage: 50 + max_mem_usage: TEST_QUEUE_MAX_MEM }, verifier::Options { minimal_gas_price: 1.into(), @@ -999,7 +1005,7 @@ fn should_reject_early_in_case_gas_price_is_less_than_min_effective() { txpool::Options { max_count: 1, max_per_sender: 2, - max_mem_usage: 50 + max_mem_usage: TEST_QUEUE_MAX_MEM }, verifier::Options { minimal_gas_price: 1.into(), @@ -1039,7 +1045,7 @@ fn should_not_reject_early_in_case_gas_price_is_less_than_min_effective() { txpool::Options { max_count: 1, max_per_sender: 2, - max_mem_usage: 50 + max_mem_usage: TEST_QUEUE_MAX_MEM }, verifier::Options { minimal_gas_price: 1.into(), diff --git a/util/version/Cargo.toml b/util/version/Cargo.toml index 71b513127..7480bb0b5 100644 --- a/util/version/Cargo.toml +++ b/util/version/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "parity-version" # NOTE: this value is used for Parity Ethereum version string (via env CARGO_PKG_VERSION) -version = "2.2.4" +version = "2.2.5" authors = ["Parity Technologies "] build = "build.rs"