diff --git a/ethcore/res/ethereum/kovan.json b/ethcore/res/ethereum/kovan.json index 71eab873d..7ce019ee8 100644 --- a/ethcore/res/ethereum/kovan.json +++ b/ethcore/res/ethereum/kovan.json @@ -24,6 +24,7 @@ }, "validateScoreTransition": 1000000, "validateStepTransition": 1500000, + "maximumUncleCountTransition": 10000000, "maximumUncleCount": 2 } } diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index d701a17eb..ddf72be2c 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -380,8 +380,13 @@ impl<'x> OpenBlock<'x> { /// NOTE Will check chain constraints and the uncle number but will NOT check /// that the header itself is actually valid. pub fn push_uncle(&mut self, valid_uncle_header: Header) -> Result<(), BlockError> { - if self.block.uncles.len() + 1 > self.engine.maximum_uncle_count() { - return Err(BlockError::TooManyUncles(OutOfBounds{min: None, max: Some(self.engine.maximum_uncle_count()), found: self.block.uncles.len() + 1})); + let max_uncles = self.engine.maximum_uncle_count(self.block.header().number()); + if self.block.uncles.len() + 1 > max_uncles { + return Err(BlockError::TooManyUncles(OutOfBounds{ + min: None, + max: Some(max_uncles), + found: self.block.uncles.len() + 1, + })); } // TODO: check number // TODO: check not a direct ancestor (use last_hashes for that) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index c78c230ca..6b5dbe59c 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -1870,7 +1870,7 @@ impl MiningBlockChainClient for Client { .find_uncle_headers(&h, engine.maximum_uncle_age()) .unwrap_or_else(Vec::new) .into_iter() - .take(engine.maximum_uncle_count()) + .take(engine.maximum_uncle_count(open_block.header().number())) .foreach(|h| { open_block.push_uncle(h).expect("pushing maximum_uncle_count; open_block was just created; @@ -1885,7 +1885,7 @@ impl MiningBlockChainClient for Client { fn reopen_block(&self, block: ClosedBlock) -> OpenBlock { let engine = &*self.engine; let mut block = block.reopen(engine); - let max_uncles = engine.maximum_uncle_count(); + let max_uncles = engine.maximum_uncle_count(block.header().number()); if block.uncles().len() < max_uncles { let chain = self.chain.read(); let h = chain.best_block_hash(); diff --git a/ethcore/src/engines/authority_round/mod.rs b/ethcore/src/engines/authority_round/mod.rs index 8c693ca07..c0d607e98 100644 --- a/ethcore/src/engines/authority_round/mod.rs +++ b/ethcore/src/engines/authority_round/mod.rs @@ -65,6 +65,8 @@ pub struct AuthorityRoundParams { pub immediate_transitions: bool, /// Block reward in base units. pub block_reward: U256, + /// Number of accepted uncles transition block. + pub maximum_uncle_count_transition: u64, /// Number of accepted uncles. pub maximum_uncle_count: usize, } @@ -79,6 +81,7 @@ impl From for AuthorityRoundParams { validate_step_transition: p.validate_step_transition.map_or(0, Into::into), immediate_transitions: p.immediate_transitions.unwrap_or(false), block_reward: p.block_reward.map_or_else(Default::default, Into::into), + maximum_uncle_count_transition: p.maximum_uncle_count_transition.map_or(0, Into::into), maximum_uncle_count: p.maximum_uncle_count.map_or(0, Into::into), } } @@ -221,6 +224,7 @@ pub struct AuthorityRound { epoch_manager: Mutex, immediate_transitions: bool, block_reward: U256, + maximum_uncle_count_transition: u64, maximum_uncle_count: usize, machine: EthereumMachine, } @@ -369,6 +373,7 @@ impl AuthorityRound { epoch_manager: Mutex::new(EpochManager::blank()), immediate_transitions: our_params.immediate_transitions, block_reward: our_params.block_reward, + maximum_uncle_count_transition: our_params.maximum_uncle_count_transition, maximum_uncle_count: our_params.maximum_uncle_count, machine: machine, }); @@ -441,7 +446,14 @@ impl Engine for AuthorityRound { ] } - fn maximum_uncle_count(&self) -> usize { self.maximum_uncle_count } + fn maximum_uncle_count(&self, block: BlockNumber) -> usize { + if block >= self.maximum_uncle_count_transition { + self.maximum_uncle_count + } else { + // fallback to default value + 2 + } + } fn populate_from_parent(&self, header: &mut Header, parent: &Header) { // Chain scoring: total weight is sqrt(U256::max_value())*height - step @@ -956,6 +968,7 @@ mod tests { validate_score_transition: 0, validate_step_transition: 0, immediate_transitions: true, + maximum_uncle_count_transition: 0, maximum_uncle_count: 0, block_reward: Default::default(), }; @@ -984,4 +997,31 @@ mod tests { assert!(aura.verify_block_family(&header, &parent_header).is_ok()); assert_eq!(last_benign.load(AtomicOrdering::SeqCst), 1); } + + #[test] + fn test_uncles_transition() { + let last_benign = Arc::new(AtomicUsize::new(0)); + let params = AuthorityRoundParams { + step_duration: Default::default(), + 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, + block_reward: 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() + }; + + assert_eq!(aura.maximum_uncle_count(0), 2); + assert_eq!(aura.maximum_uncle_count(1), 0); + assert_eq!(aura.maximum_uncle_count(100), 0); + } } diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index 89de861bb..7c12e138e 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -192,7 +192,8 @@ pub trait Engine: Sync + Send { fn extra_info(&self, _header: &M::Header) -> BTreeMap { BTreeMap::new() } /// Maximum number of uncles a block is allowed to declare. - fn maximum_uncle_count(&self) -> usize { 0 } + fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 } + /// The number of generations back that uncles can be. fn maximum_uncle_age(&self) -> usize { 6 } @@ -363,7 +364,7 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> { } /// The nonce with which accounts begin at given block. - fn account_start_nonce(&self, block: u64) -> U256 { + fn account_start_nonce(&self, block: BlockNumber) -> U256 { self.machine().account_start_nonce(block) } diff --git a/ethcore/src/engines/null_engine.rs b/ethcore/src/engines/null_engine.rs index 518f0f8f5..cc48a225a 100644 --- a/ethcore/src/engines/null_engine.rs +++ b/ethcore/src/engines/null_engine.rs @@ -16,6 +16,7 @@ use bigint::prelude::U256; use engines::Engine; +use header::BlockNumber; use parity_machine::{Header, LiveBlock, WithBalances}; /// Params for a null engine. @@ -95,7 +96,7 @@ impl Engine for NullEngine { self.machine.note_rewards(block, &[(author, result_block_reward)], &uncle_rewards) } - fn maximum_uncle_count(&self) -> usize { 2 } + fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 } fn verify_local_seal(&self, _header: &M::Header) -> Result<(), M::Error> { Ok(()) diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index 97cc18c03..7fe1f68bf 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -450,7 +450,7 @@ impl Engine for Tendermint { fn machine(&self) -> &EthereumMachine { &self.machine } - fn maximum_uncle_count(&self) -> usize { 0 } + fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 } fn maximum_uncle_age(&self) -> usize { 0 } diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index 9548fe511..55f0d58de 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -26,7 +26,7 @@ use util::Address; use unexpected::{OutOfBounds, Mismatch}; use block::*; use error::{BlockError, Error}; -use header::Header; +use header::{Header, BlockNumber}; use engines::{self, Engine}; use ethjson; use rlp::{self, UntrustedRlp}; @@ -181,7 +181,7 @@ impl Engine for Arc { } } - fn maximum_uncle_count(&self) -> usize { 2 } + fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 } fn populate_from_parent(&self, header: &mut Header, parent: &Header) { let difficulty = self.calculate_difficulty(header, parent); @@ -520,7 +520,7 @@ mod tests { let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); assert_eq!(15, eras); assert_eq!(U256::from_str("271000000000000").unwrap(), reward); - + let block_number = 250000000; let (eras, reward) = ecip1017_eras_block_reward(eras_rounds, start_reward, block_number); assert_eq!(49, eras); diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index c56641916..2978774b0 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -137,9 +137,14 @@ pub fn verify_block_family(header: &Header, parent: &Header, engine: &EthEngine, fn verify_uncles(header: &Header, bytes: &[u8], bc: &BlockProvider, engine: &EthEngine) -> Result<(), Error> { let num_uncles = UntrustedRlp::new(bytes).at(2)?.item_count()?; + let max_uncles = engine.maximum_uncle_count(header.number()); if num_uncles != 0 { - if num_uncles > engine.maximum_uncle_count() { - return Err(From::from(BlockError::TooManyUncles(OutOfBounds { min: None, max: Some(engine.maximum_uncle_count()), found: num_uncles }))); + if num_uncles > max_uncles { + return Err(From::from(BlockError::TooManyUncles(OutOfBounds { + min: None, + max: Some(max_uncles), + found: num_uncles, + }))); } let mut excluded = HashSet::new(); @@ -653,7 +658,7 @@ mod tests { let mut bad_uncles = good_uncles.clone(); bad_uncles.push(good_uncle1.clone()); check_fail(family_test(&create_test_block_with_data(&header, &good_transactions, &bad_uncles), engine, &bc), - TooManyUncles(OutOfBounds { max: Some(engine.maximum_uncle_count()), min: None, found: bad_uncles.len() })); + TooManyUncles(OutOfBounds { max: Some(engine.maximum_uncle_count(header.number())), min: None, found: bad_uncles.len() })); header = good.clone(); bad_uncles = vec![ good_uncle1.clone(), good_uncle1.clone() ]; diff --git a/json/src/spec/authority_round.rs b/json/src/spec/authority_round.rs index b3e75a6b4..f39db7344 100644 --- a/json/src/spec/authority_round.rs +++ b/json/src/spec/authority_round.rs @@ -43,6 +43,10 @@ pub struct AuthorityRoundParams { /// Reward per block in wei. #[serde(rename="blockReward")] pub block_reward: Option, + /// Block at which maximum uncle count should be considered. + #[serde(rename="maximumUncleCountTransition")] + pub maximum_uncle_count_transition: Option, + /// Maximum number of accepted uncles. #[serde(rename="maximumUncleCount")] pub maximum_uncle_count: Option, } @@ -73,7 +77,9 @@ mod tests { }, "startStep" : 24, "validateStepTransition": 150, - "blockReward": 5000000 + "blockReward": 5000000, + "maximumUncleCountTransition": 10000000, + "maximumUncleCount": 5 } }"#; @@ -82,6 +88,8 @@ mod tests { assert_eq!(deserialized.params.validators, ValidatorSet::List(vec![Address(H160::from("0xc6d9d2cd449a754c494264e1809c50e34d64562b"))])); assert_eq!(deserialized.params.start_step, Some(Uint(U256::from(24)))); assert_eq!(deserialized.params.immediate_transitions, None); + assert_eq!(deserialized.params.maximum_uncle_count_transition, Some(Uint(10_000_000.into()))); + assert_eq!(deserialized.params.maximum_uncle_count, Some(Uint(5.into()))); } }