diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index c0ae90bb4e6..ee9271e8e8b 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -257,6 +257,7 @@ impl<'x> OpenBlock<'x> { tracing: bool, db: StateDB, parent: &Header, + parent_uncles: usize, last_hashes: Arc, author: Address, gas_range_target: (U256, U256), @@ -278,7 +279,7 @@ impl<'x> OpenBlock<'x> { let gas_floor_target = cmp::max(gas_range_target.0, engine.params().min_gas_limit); let gas_ceil_target = cmp::max(gas_range_target.1, gas_floor_target); - engine.populate_from_parent(&mut r.block.header, parent, gas_floor_target, gas_ceil_target); + engine.populate_from_parent(&mut r.block.header, parent, parent_uncles, gas_floor_target, gas_ceil_target); engine.on_new_block(&mut r.block); Ok(r) } @@ -534,6 +535,7 @@ pub fn enact( tracing: bool, db: StateDB, parent: &Header, + parent_uncles: usize, last_hashes: Arc, factories: Factories, ) -> Result { @@ -545,7 +547,7 @@ pub fn enact( } } - let mut b = OpenBlock::new(engine, factories, tracing, db, parent, last_hashes, Address::new(), (3141562.into(), 31415620.into()), vec![])?; + let mut b = OpenBlock::new(engine, factories, tracing, db, parent, parent_uncles, last_hashes, Address::new(), (3141562.into(), 31415620.into()), vec![])?; b.set_difficulty(*header.difficulty()); b.set_gas_limit(*header.gas_limit()); b.set_timestamp(header.timestamp()); @@ -598,11 +600,12 @@ pub fn enact_verified( tracing: bool, db: StateDB, parent: &Header, + parent_uncles: usize, last_hashes: Arc, factories: Factories, ) -> Result { let view = BlockView::new(&block.bytes); - enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, factories) + enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, parent_uncles, last_hashes, factories) } #[cfg(test)] @@ -628,6 +631,7 @@ mod tests { tracing: bool, db: StateDB, parent: &Header, + parent_uncles: usize, last_hashes: Arc, factories: Factories, ) -> Result { @@ -635,7 +639,7 @@ mod tests { let header = block.header(); let transactions: Result, Error> = block.transactions().into_iter().map(SignedTransaction::new).collect(); let transactions = transactions?; - enact(&header, &transactions, &block.uncles(), engine, tracing, db, parent, last_hashes, factories) + enact(&header, &transactions, &block.uncles(), engine, tracing, db, parent, parent_uncles, last_hashes, factories) } /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards @@ -646,11 +650,12 @@ mod tests { tracing: bool, db: StateDB, parent: &Header, + parent_uncles: usize, last_hashes: Arc, factories: Factories, ) -> Result { let header = BlockView::new(block_bytes).header_view(); - Ok(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, factories)?.seal(engine, header.seal())?) + Ok(enact_bytes(block_bytes, engine, tracing, db, parent, parent_uncles, last_hashes, factories)?.seal(engine, header.seal())?) } #[test] @@ -661,7 +666,8 @@ mod tests { let mut db_result = get_temp_state_db(); let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(&*spec.engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let parent_uncles = 0; + let b = OpenBlock::new(&*spec.engine, Default::default(), false, db, &genesis_header, parent_uncles, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close_and_lock(); let _ = b.seal(&*spec.engine, vec![]); } @@ -676,14 +682,16 @@ mod tests { let mut db_result = get_temp_state_db(); let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap() + let parent_uncles = 0; + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, parent_uncles, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap() .close_and_lock().seal(engine, vec![]).unwrap(); let orig_bytes = b.rlp_bytes(); let orig_db = b.drain(); let mut db_result = get_temp_state_db(); let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); - let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, Default::default()).unwrap(); + let parent_uncles = 0; + let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, parent_uncles, last_hashes, Default::default()).unwrap(); assert_eq!(e.rlp_bytes(), orig_bytes); @@ -702,7 +710,8 @@ mod tests { let mut db_result = get_temp_state_db(); let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let mut open_block = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let parent_uncles = 0; + let mut open_block = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, parent_uncles, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let mut uncle1_header = Header::new(); uncle1_header.set_extra_data(b"uncle1".to_vec()); let mut uncle2_header = Header::new(); @@ -716,7 +725,8 @@ mod tests { let mut db_result = get_temp_state_db(); let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); - let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, Default::default()).unwrap(); + let parent_uncles = 0; + let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, parent_uncles, last_hashes, Default::default()).unwrap(); let bytes = e.rlp_bytes(); assert_eq!(bytes, orig_bytes); diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 8986dc6b881..e21b133598f 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -101,6 +101,11 @@ pub trait BlockProvider { self.block_body(hash).map(|body| body.uncle_hashes()) } + /// Get number of uncles for a given block. + fn uncles_count(&self, hash: &H256) -> Option { + self.block_body(hash).map(|body| body.uncles_count()) + } + /// Get the number of given block's hash. fn block_number(&self, hash: &H256) -> Option { self.block_details(hash).map(|details| details.number) diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 06889a538ea..7c1419fd81a 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -385,9 +385,11 @@ impl Client { if let Some(parent) = chain_has_parent { // Enact Verified Block let last_hashes = self.build_last_hashes(header.parent_hash().clone()); + let uncles_count = chain.uncles_count(header.parent_hash()) + .expect("chain_has_parent.is_some() so block body must exist; qed"); let db = self.state_db.lock().boxed_clone_canon(header.parent_hash()); - let enact_result = enact_verified(block, engine, self.tracedb.read().tracing_enabled(), db, &parent, last_hashes, self.factories.clone()); + let enact_result = enact_verified(block, engine, self.tracedb.read().tracing_enabled(), db, &parent, uncles_count, last_hashes, self.factories.clone()); let locked_block = enact_result.map_err(|e| { warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); })?; @@ -1510,6 +1512,7 @@ impl MiningBlockChainClient for Client { false, // TODO: this will need to be parameterised once we want to do immediate mining insertion. self.state_db.lock().boxed_clone_canon(&h), &chain.block_header(&h).expect("h is best block hash: so its header must exist: qed"), + chain.uncles_count(&h).expect("h is best block hash; qed"), self.build_last_hashes(h.clone()), author, gas_range_target, diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index d96b305de44..98cfc0bb011 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -358,6 +358,7 @@ impl MiningBlockChainClient for TestBlockChainClient { fn prepare_open_block(&self, author: Address, gas_range_target: (U256, U256), extra_data: Bytes) -> OpenBlock { let engine = &*self.spec.engine; let genesis_header = self.spec.genesis_header(); + let parent_uncles = 0; let mut db_result = get_temp_state_db(); let db = self.spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); @@ -368,6 +369,7 @@ impl MiningBlockChainClient for TestBlockChainClient { false, db, &genesis_header, + parent_uncles, Arc::new(last_hashes), author, gas_range_target, diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index 03b5d785fa9..2b42e7ffa70 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -234,7 +234,7 @@ impl Engine for AuthorityRound { Schedule::new_post_eip150(usize::max_value(), true, true, true) } - fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { + fn populate_from_parent(&self, header: &mut Header, parent: &Header, _parent_uncles: usize, gas_floor_target: U256, _gas_ceil_target: U256) { // Chain scoring: total weight is sqrt(U256::max_value())*height - step let new_difficulty = U256::from(U128::max_value()) + header_step(parent).expect("Header has been verified; qed").into() - self.step.load(AtomicOrdering::SeqCst).into(); header.set_difficulty(new_difficulty); @@ -305,7 +305,7 @@ impl Engine for AuthorityRound { } /// Do the validator and gas limit validation. - fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + fn verify_block_family(&self, header: &Header, parent: &Header, _parent_uncles: usize, _block: Option<&[u8]>) -> Result<(), Error> { let step = header_step(header)?; // Give one step slack if step is lagging, double vote is still not possible. if self.is_future_step(step) { @@ -413,7 +413,7 @@ mod tests { let mut header: Header = Header::default(); header.set_seal(vec![encode(&H520::default()).to_vec()]); - let verify_result = engine.verify_block_family(&header, &Default::default(), None); + let verify_result = engine.verify_block_family(&header, &Default::default(), 0, None); assert!(verify_result.is_err()); } @@ -429,9 +429,10 @@ mod tests { let db1 = spec.ensure_db_good(get_temp_state_db().take(), &Default::default()).unwrap(); let db2 = spec.ensure_db_good(get_temp_state_db().take(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![]).unwrap(); + let parent_uncles = 0; + let b1 = OpenBlock::new(engine, Default::default(), false, db1, &genesis_header, parent_uncles, last_hashes.clone(), addr1, (3141562.into(), 31415620.into()), vec![]).unwrap(); let b1 = b1.close_and_lock(); - let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![]).unwrap(); + let b2 = OpenBlock::new(engine, Default::default(), false, db2, &genesis_header, parent_uncles, last_hashes, addr2, (3141562.into(), 31415620.into()), vec![]).unwrap(); let b2 = b2.close_and_lock(); engine.set_signer(tap.clone(), addr1, "1".into()); @@ -467,9 +468,9 @@ mod tests { // Two validators. // Spec starts with step 2. header.set_seal(vec![encode(&2usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); - assert!(engine.verify_block_family(&header, &parent_header, None).is_err()); + assert!(engine.verify_block_family(&header, &parent_header, 0, None).is_err()); header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); - assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(engine.verify_block_family(&header, &parent_header, 0, None).is_ok()); } #[test] @@ -491,8 +492,8 @@ mod tests { // Two validators. // Spec starts with step 2. header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); - assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(engine.verify_block_family(&header, &parent_header, 0, None).is_ok()); header.set_seal(vec![encode(&5usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]); - assert!(engine.verify_block_family(&header, &parent_header, None).is_err()); + assert!(engine.verify_block_family(&header, &parent_header, 0, None).is_err()); } } diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 34b89b2d65b..c884cd40ffb 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -90,7 +90,7 @@ impl Engine for BasicAuthority { Schedule::new_homestead() } - fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { + fn populate_from_parent(&self, header: &mut Header, parent: &Header, _parent_uncles: usize, gas_floor_target: U256, _gas_ceil_target: U256) { header.set_difficulty(parent.difficulty().clone()); header.set_gas_limit({ let gas_limit = parent.gas_limit().clone(); @@ -137,7 +137,7 @@ impl Engine for BasicAuthority { Ok(()) } - fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { + fn verify_block_family(&self, header: &Header, parent: &Header, _parent_uncles: usize, _block: Option<&[u8]>) -> result::Result<(), Error> { use rlp::{UntrustedRlp, View}; // Check if the signature belongs to a validator, can depend on parent state. let sig = UntrustedRlp::new(&header.seal()[0]).as_val::()?; @@ -239,7 +239,7 @@ mod tests { let mut header: Header = Header::default(); header.set_seal(vec![::rlp::encode(&H520::default()).to_vec()]); - let verify_result = engine.verify_block_family(&header, &Default::default(), None); + let verify_result = engine.verify_block_family(&header, &Default::default(), 0, None); assert!(verify_result.is_err()); } @@ -255,7 +255,8 @@ mod tests { let mut db_result = get_temp_state_db(); let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap(); + let parent_uncles = 0; + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, parent_uncles, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close_and_lock(); if let Seal::Regular(seal) = engine.generate_seal(b.block()) { assert!(b.try_seal(engine, seal).is_ok()); diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index 45bede9f451..ca8984a36cc 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -86,7 +86,8 @@ mod tests { let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); let genesis_header = spec.genesis_header(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let parent_uncles = 0; + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, parent_uncles, last_hashes, Address::default(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close_and_lock(); if let Seal::Regular(seal) = engine.generate_seal(b.block()) { assert!(b.try_seal(engine, seal).is_ok()); diff --git a/ethcore/src/engines/mod.rs b/ethcore/src/engines/mod.rs index f292a06d8e5..886f6a72c88 100644 --- a/ethcore/src/engines/mod.rs +++ b/ethcore/src/engines/mod.rs @@ -150,7 +150,7 @@ pub trait Engine : Sync + Send { /// Phase 3 verification. Check block information against parent and uncles. `block` (the header's full block) /// may be provided for additional checks. Returns either a null `Ok` or a general error detailing the problem with import. - fn verify_block_family(&self, _header: &Header, _parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } + fn verify_block_family(&self, _header: &Header, _parent: &Header, _parent_uncles: usize, _block: Option<&[u8]>) -> Result<(), Error> { Ok(()) } /// Additional verification for transactions in blocks. // TODO: Add flags for which bits of the transaction to check. @@ -180,7 +180,7 @@ pub trait Engine : Sync + Send { /// Populate a header's fields based on its parent's header. /// Usually implements the chain scoring rule based on weight. /// The gas floor target must not be lower than the engine's minimum gas limit. - fn populate_from_parent(&self, header: &mut Header, parent: &Header, _gas_floor_target: U256, _gas_ceil_target: U256) { + fn populate_from_parent(&self, header: &mut Header, parent: &Header, _parent_uncles: usize, _gas_floor_target: U256, _gas_ceil_target: U256) { header.set_difficulty(parent.difficulty().clone()); header.set_gas_limit(parent.gas_limit().clone()); } diff --git a/ethcore/src/engines/tendermint/mod.rs b/ethcore/src/engines/tendermint/mod.rs index 2d2714e9724..0f21c6e7fcd 100644 --- a/ethcore/src/engines/tendermint/mod.rs +++ b/ethcore/src/engines/tendermint/mod.rs @@ -405,7 +405,7 @@ impl Engine for Tendermint { Schedule::new_post_eip150(usize::max_value(), true, true, true) } - fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, _gas_ceil_target: U256) { + fn populate_from_parent(&self, header: &mut Header, parent: &Header, _parent_uncles: usize, gas_floor_target: U256, _gas_ceil_target: U256) { // Chain scoring: total weight is sqrt(U256::max_value())*height - view let new_difficulty = U256::from(U128::max_value()) + consensus_view(parent).expect("Header has been verified; qed").into() - self.view.load(AtomicOrdering::SeqCst).into(); header.set_difficulty(new_difficulty); @@ -514,7 +514,7 @@ impl Engine for Tendermint { } /// Verify validators and gas limit. - fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> Result<(), Error> { + fn verify_block_family(&self, header: &Header, parent: &Header, _parent_uncles: usize, _block: Option<&[u8]>) -> Result<(), Error> { let proposal = ConsensusMessage::new_proposal(header)?; let proposer = proposal.verify()?; if !self.is_authority(&proposer) { @@ -680,7 +680,8 @@ mod tests { let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); let genesis_header = spec.genesis_header(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![]).unwrap(); + let parent_uncles = 0; + let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, parent_uncles, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close(); if let Seal::Proposal(seal) = spec.engine.generate_seal(b.block()) { (b, seal) @@ -783,14 +784,14 @@ mod tests { let seal = proposal_seal(&tap, &header, 0); header.set_seal(seal); // Good proposer. - assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(engine.verify_block_family(&header, &parent_header, 0, None).is_ok()); let validator = insert_and_unlock(&tap, "0"); header.set_author(validator); let seal = proposal_seal(&tap, &header, 0); header.set_seal(seal); // Bad proposer. - match engine.verify_block_family(&header, &parent_header, None) { + match engine.verify_block_family(&header, &parent_header, 0, None) { Err(Error::Engine(EngineError::NotProposer(_))) => {}, _ => panic!(), } @@ -800,7 +801,7 @@ mod tests { let seal = proposal_seal(&tap, &header, 0); header.set_seal(seal); // Not authority. - match engine.verify_block_family(&header, &parent_header, None) { + match engine.verify_block_family(&header, &parent_header, 0, None) { Err(Error::Engine(EngineError::NotAuthorized(_))) => {}, _ => panic!(), }; @@ -829,7 +830,7 @@ mod tests { header.set_seal(seal.clone()); // One good signature is not enough. - match engine.verify_block_family(&header, &parent_header, None) { + match engine.verify_block_family(&header, &parent_header, 0, None) { Err(Error::Engine(EngineError::BadSealFieldSize(_))) => {}, _ => panic!(), } @@ -840,7 +841,7 @@ mod tests { seal[2] = ::rlp::encode_list(&vec![H520::from(signature1.clone()), H520::from(signature0.clone())]).to_vec(); header.set_seal(seal.clone()); - assert!(engine.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(engine.verify_block_family(&header, &parent_header, 0, None).is_ok()); let bad_voter = insert_and_unlock(&tap, "101"); let bad_signature = tap.sign(bad_voter, None, vote_info.sha3()).unwrap(); @@ -849,7 +850,7 @@ mod tests { header.set_seal(seal); // One good and one bad signature. - match engine.verify_block_family(&header, &parent_header, None) { + match engine.verify_block_family(&header, &parent_header, 0, None) { Err(Error::Engine(EngineError::NotAuthorized(_))) => {}, _ => panic!(), }; diff --git a/ethcore/src/engines/validator_set/contract.rs b/ethcore/src/engines/validator_set/contract.rs index adfdd1225c0..1c6748527de 100644 --- a/ethcore/src/engines/validator_set/contract.rs +++ b/ethcore/src/engines/validator_set/contract.rs @@ -180,7 +180,7 @@ mod tests { header.set_parent_hash(client.chain_info().best_block_hash); // `reportBenign` when the designated proposer releases block from the future (bad clock). - assert!(client.engine().verify_block_family(&header, &header, None).is_err()); + assert!(client.engine().verify_block_family(&header, &header, 0, None).is_err()); // Seal a block. client.engine().step(); assert_eq!(client.chain_info().best_block_number, 1); @@ -188,7 +188,7 @@ mod tests { assert_eq!(client.call_contract(BlockId::Latest, validator_contract, "d8f2e0bf".from_hex().unwrap()).unwrap().to_hex(), "0000000000000000000000007d577a597b2742b498cb5cf0c26cdcd726d39e6e"); // Simulate a misbehaving validator by handling a double proposal. let header = client.best_block_header().decode(); - assert!(client.engine().verify_block_family(&header, &header, None).is_err()); + assert!(client.engine().verify_block_family(&header, &header, 0, None).is_err()); // Seal a block. client.engine().step(); client.engine().step(); diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index fbb9c8a5ca4..6b1a69d0bc1 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -43,6 +43,8 @@ pub struct EthashParams { pub difficulty_bound_divisor: U256, /// Difficulty increment divisor. pub difficulty_increment_divisor: u64, + /// Metropolis difficulty increment divisor. + pub metropolis_difficulty_increment_divisor: u64, /// Block duration. pub duration_limit: u64, /// Block reward. @@ -63,6 +65,8 @@ pub struct EthashParams { pub difficulty_hardfork_bound_divisor: U256, /// Block on which there is no additional difficulty from the exponential bomb. pub bomb_defuse_transition: u64, + /// Number of first block where EIP-100 rules begin. + pub eip100_transition: u64, /// Number of first block where EIP-150 rules begin. pub eip150_transition: u64, /// Number of first block where EIP-155 rules begin. @@ -96,6 +100,7 @@ impl From for EthashParams { minimum_difficulty: p.minimum_difficulty.into(), difficulty_bound_divisor: p.difficulty_bound_divisor.into(), difficulty_increment_divisor: p.difficulty_increment_divisor.map_or(10, Into::into), + metropolis_difficulty_increment_divisor: p.metropolis_difficulty_increment_divisor.map_or(9, Into::into), duration_limit: p.duration_limit.into(), block_reward: p.block_reward.into(), registrar: p.registrar.map_or_else(Address::new, Into::into), @@ -106,6 +111,7 @@ impl From for EthashParams { difficulty_hardfork_transition: p.difficulty_hardfork_transition.map_or(u64::max_value(), Into::into), difficulty_hardfork_bound_divisor: p.difficulty_hardfork_bound_divisor.map_or(p.difficulty_bound_divisor.into(), Into::into), bomb_defuse_transition: p.bomb_defuse_transition.map_or(u64::max_value(), Into::into), + eip100_transition: p.eip100_transition.map_or(u64::max_value(), Into::into), eip150_transition: p.eip150_transition.map_or(0, Into::into), eip155_transition: p.eip155_transition.map_or(0, Into::into), eip160_transition: p.eip160_transition.map_or(0, Into::into), @@ -186,8 +192,8 @@ impl Engine for Ethash { } } - fn populate_from_parent(&self, header: &mut Header, parent: &Header, gas_floor_target: U256, mut gas_ceil_target: U256) { - let difficulty = self.calculate_difficulty(header, parent); + fn populate_from_parent(&self, header: &mut Header, parent: &Header, parent_uncles: usize, gas_floor_target: U256, mut gas_ceil_target: U256) { + let difficulty = self.calculate_difficulty(header, parent, parent_uncles); if header.number() >= self.ethash_params.max_gas_limit_transition && gas_ceil_target > self.ethash_params.max_gas_limit { warn!("Gas limit target is limited to {}", self.ethash_params.max_gas_limit); gas_ceil_target = self.ethash_params.max_gas_limit; @@ -338,14 +344,14 @@ impl Engine for Ethash { Ok(()) } - fn verify_block_family(&self, header: &Header, parent: &Header, _block: Option<&[u8]>) -> result::Result<(), Error> { + fn verify_block_family(&self, header: &Header, parent: &Header, parent_uncles: usize, _block: Option<&[u8]>) -> result::Result<(), Error> { // we should not calculate difficulty for genesis blocks if header.number() == 0 { return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))); } // Check difficulty is correct given the two timestamps. - let expected_difficulty = self.calculate_difficulty(header, parent); + let expected_difficulty = self.calculate_difficulty(header, parent, parent_uncles); if header.difficulty() != &expected_difficulty { return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() }))) } @@ -400,7 +406,7 @@ fn round_block_gas_limit(gas_limit: U256, lower_limit: U256, upper_limit: U256) #[cfg_attr(feature="dev", allow(wrong_self_convention))] impl Ethash { - fn calculate_difficulty(&self, header: &Header, parent: &Header) -> U256 { + fn calculate_difficulty(&self, header: &Header, parent: &Header, parent_uncles: usize) -> U256 { const EXP_DIFF_PERIOD: u64 = 100000; if header.number() == 0 { panic!("Can't calculate genesis block difficulty"); @@ -417,19 +423,24 @@ impl Ethash { let mut target = if header.number() < frontier_limit { if header.timestamp() >= parent.timestamp() + duration_limit { - parent.difficulty().clone() - (parent.difficulty().clone() / difficulty_bound_divisor) + *parent.difficulty() - (*parent.difficulty() / difficulty_bound_divisor) } else { - parent.difficulty().clone() + (parent.difficulty().clone() / difficulty_bound_divisor) + *parent.difficulty() + (*parent.difficulty() / difficulty_bound_divisor) } } else { trace!(target: "ethash", "Calculating difficulty parent.difficulty={}, header.timestamp={}, parent.timestamp={}", parent.difficulty(), header.timestamp(), parent.timestamp()); //block_diff = parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99) - let diff_inc = (header.timestamp() - parent.timestamp()) / self.ethash_params.difficulty_increment_divisor; - if diff_inc <= 1 { - parent.difficulty().clone() + parent.difficulty().clone() / From::from(difficulty_bound_divisor) * From::from(1 - diff_inc) + let (increment_divisor, threshold) = if header.number() < self.ethash_params.eip100_transition { + (self.ethash_params.difficulty_increment_divisor, 1) } else { - parent.difficulty().clone() - parent.difficulty().clone() / From::from(difficulty_bound_divisor) * From::from(min(diff_inc - 1, 99)) + (self.ethash_params.metropolis_difficulty_increment_divisor, 1 + cmp::min(parent_uncles as u64, 1)) + }; + let diff_inc = (header.timestamp() - parent.timestamp()) / increment_divisor; + if diff_inc <= threshold { + *parent.difficulty() + *parent.difficulty() / difficulty_bound_divisor.into() * (threshold - diff_inc).into() + } else { + *parent.difficulty() - *parent.difficulty() / difficulty_bound_divisor.into() * min(diff_inc - threshold, 99).into() } }; target = max(min_difficulty, target); @@ -511,7 +522,8 @@ mod tests { let mut db_result = get_temp_state_db(); let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let parent_uncles = 0; + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, parent_uncles, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close(); assert_eq!(b.state().balance(&Address::zero()).unwrap(), U256::from_str("4563918244f40000").unwrap()); } @@ -524,7 +536,8 @@ mod tests { let mut db_result = get_temp_state_db(); let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let parent_uncles = 0; + let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, parent_uncles, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let mut uncle = Header::new(); let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into(); uncle.set_author(uncle_author); @@ -666,7 +679,7 @@ mod tests { let header: Header = Header::default(); let parent_header: Header = Header::default(); - let verify_result = engine.verify_block_family(&header, &parent_header, None); + let verify_result = engine.verify_block_family(&header, &parent_header, 0, None); match verify_result { Err(Error::Block(BlockError::RidiculousNumber(_))) => {}, @@ -683,7 +696,7 @@ mod tests { let mut parent_header: Header = Header::default(); parent_header.set_number(1); - let verify_result = engine.verify_block_family(&header, &parent_header, None); + let verify_result = engine.verify_block_family(&header, &parent_header, 0, None); match verify_result { Err(Error::Block(BlockError::InvalidDifficulty(_))) => {}, @@ -701,7 +714,7 @@ mod tests { let mut parent_header: Header = Header::default(); parent_header.set_number(1); - let verify_result = engine.verify_block_family(&header, &parent_header, None); + let verify_result = engine.verify_block_family(&header, &parent_header, 0, None); match verify_result { Err(Error::Block(BlockError::InvalidGasLimit(_))) => {}, @@ -733,8 +746,9 @@ mod tests { let mut header = Header::default(); header.set_number(parent_header.number() + 1); header.set_timestamp(1455404058); + let parent_uncles = 0; - let difficulty = ethash.calculate_difficulty(&header, &parent_header); + let difficulty = ethash.calculate_difficulty(&header, &parent_header, parent_uncles); assert_eq!(U256::from_str("b6b4bbd735f").unwrap(), difficulty); } @@ -751,8 +765,9 @@ mod tests { let mut header = Header::default(); header.set_number(parent_header.number() + 1); header.set_timestamp(1463003177); + let parent_uncles = 0; - let difficulty = ethash.calculate_difficulty(&header, &parent_header); + let difficulty = ethash.calculate_difficulty(&header, &parent_header, parent_uncles); assert_eq!(U256::from_str("1fc50f118efe").unwrap(), difficulty); } @@ -775,17 +790,17 @@ mod tests { header.set_timestamp(parent_header.timestamp() + 20); assert_eq!( U256::from_str("6F55FE9B74B").unwrap(), - ethash.calculate_difficulty(&header, &parent_header) + ethash.calculate_difficulty(&header, &parent_header, 0) ); header.set_timestamp(parent_header.timestamp() + 5); assert_eq!( U256::from_str("6F71D75632D").unwrap(), - ethash.calculate_difficulty(&header, &parent_header) + ethash.calculate_difficulty(&header, &parent_header, 0) ); header.set_timestamp(parent_header.timestamp() + 80); assert_eq!( U256::from_str("6F02746B3A5").unwrap(), - ethash.calculate_difficulty(&header, &parent_header) + ethash.calculate_difficulty(&header, &parent_header, 0) ); } @@ -808,7 +823,7 @@ mod tests { header.set_timestamp(parent_header.timestamp() + 6); assert_eq!( U256::from_str("1496E6206188").unwrap(), - ethash.calculate_difficulty(&header, &parent_header) + ethash.calculate_difficulty(&header, &parent_header, 0) ); parent_header.set_number(5100123); parent_header.set_difficulty(U256::from_str("14D24B39C7CF").unwrap()); @@ -817,7 +832,7 @@ mod tests { header.set_timestamp(parent_header.timestamp() + 41); assert_eq!( U256::from_str("14CA9C5D9227").unwrap(), - ethash.calculate_difficulty(&header, &parent_header) + ethash.calculate_difficulty(&header, &parent_header, 0) ); parent_header.set_number(6150001); parent_header.set_difficulty(U256::from_str("305367B57227").unwrap()); @@ -826,7 +841,7 @@ mod tests { header.set_timestamp(parent_header.timestamp() + 105); assert_eq!( U256::from_str("309D09E0C609").unwrap(), - ethash.calculate_difficulty(&header, &parent_header) + ethash.calculate_difficulty(&header, &parent_header, 0) ); parent_header.set_number(8000000); parent_header.set_difficulty(U256::from_str("1180B36D4CE5B6A").unwrap()); @@ -835,7 +850,7 @@ mod tests { header.set_timestamp(parent_header.timestamp() + 420); assert_eq!( U256::from_str("5126FFD5BCBB9E7").unwrap(), - ethash.calculate_difficulty(&header, &parent_header) + ethash.calculate_difficulty(&header, &parent_header, 0) ); } @@ -846,31 +861,32 @@ mod tests { let mut parent = Header::new(); let mut header = Header::new(); header.set_number(1); + let parent_uncles = 0; // this test will work for this constant only assert_eq!(PARITY_GAS_LIMIT_DETERMINANT, U256::from(37)); // when parent.gas_limit < gas_floor_target: parent.set_gas_limit(U256::from(50_000)); - ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); + ethash.populate_from_parent(&mut header, &parent, parent_uncles, U256::from(100_000), U256::from(200_000)); assert_eq!(*header.gas_limit(), U256::from(50_024)); // when parent.gas_limit > gas_ceil_target: parent.set_gas_limit(U256::from(250_000)); - ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); + ethash.populate_from_parent(&mut header, &parent, parent_uncles, U256::from(100_000), U256::from(200_000)); assert_eq!(*header.gas_limit(), U256::from(249_787)); // when parent.gas_limit is in miner's range header.set_gas_used(U256::from(150_000)); parent.set_gas_limit(U256::from(150_000)); - ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(200_000)); + ethash.populate_from_parent(&mut header, &parent, parent_uncles, U256::from(100_000), U256::from(200_000)); assert_eq!(*header.gas_limit(), U256::from(150_035)); // when parent.gas_limit is in miner's range // && we can NOT increase it to be multiple of constant header.set_gas_used(U256::from(150_000)); parent.set_gas_limit(U256::from(150_000)); - ethash.populate_from_parent(&mut header, &parent, U256::from(100_000), U256::from(150_002)); + ethash.populate_from_parent(&mut header, &parent, parent_uncles, U256::from(100_000), U256::from(150_002)); assert_eq!(*header.gas_limit(), U256::from(149_998)); // when parent.gas_limit is in miner's range @@ -878,7 +894,7 @@ mod tests { // && we can NOT decrease it to be multiple of constant header.set_gas_used(U256::from(150_000)); parent.set_gas_limit(U256::from(150_000)); - ethash.populate_from_parent(&mut header, &parent, U256::from(150_000), U256::from(150_002)); + ethash.populate_from_parent(&mut header, &parent, parent_uncles, U256::from(150_000), U256::from(150_002)); assert_eq!(*header.gas_limit(), U256::from(150_002)); } @@ -895,8 +911,9 @@ mod tests { let mut header = Header::default(); header.set_number(parent_header.number() + 1); header.set_timestamp(u64::max_value()); + let parent_uncles = 0; - let difficulty = ethash.calculate_difficulty(&header, &parent_header); + let difficulty = ethash.calculate_difficulty(&header, &parent_header, parent_uncles); assert_eq!(U256::from(12543204905719u64), difficulty); } @@ -915,26 +932,26 @@ mod tests { header.set_gas_limit(100_001.into()); header.set_difficulty(ethparams.minimum_difficulty); let ethash = Ethash::new(spec.params, ethparams, BTreeMap::new()); - assert!(ethash.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(ethash.verify_block_family(&header, &parent_header, 0, None).is_ok()); parent_header.set_number(9); header.set_number(parent_header.number() + 1); parent_header.set_gas_limit(99_999.into()); header.set_gas_limit(100_000.into()); - assert!(ethash.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(ethash.verify_block_family(&header, &parent_header, 0, None).is_ok()); parent_header.set_gas_limit(200_000.into()); header.set_gas_limit(200_000.into()); - assert!(ethash.verify_block_family(&header, &parent_header, None).is_ok()); + assert!(ethash.verify_block_family(&header, &parent_header, 0, None).is_ok()); parent_header.set_gas_limit(100_000.into()); header.set_gas_limit(100_001.into()); - assert!(ethash.verify_block_family(&header, &parent_header, None).is_err()); + assert!(ethash.verify_block_family(&header, &parent_header, 0, None).is_err()); parent_header.set_gas_limit(200_000.into()); header.set_gas_limit(200_001.into()); - assert!(ethash.verify_block_family(&header, &parent_header, None).is_err()); + assert!(ethash.verify_block_family(&header, &parent_header, 0, None).is_err()); } #[test] diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 473c91e4d0f..dfd7da0d0e8 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -550,7 +550,10 @@ const POW_VERIFY_RATE: f32 = 0.02; pub fn verify_old_block(rng: &mut OsRng, header: &Header, engine: &Engine, chain: &BlockChain, body: Option<&[u8]>, always: bool) -> Result<(), ::error::Error> { if always || rng.gen::() <= POW_VERIFY_RATE { match chain.block_header(header.parent_hash()) { - Some(parent) => engine.verify_block_family(header, &parent, body), + Some(parent) => { + let parent_uncles = chain.uncles_count(parent.parent_hash()).expect("parent header exists; qed"); + engine.verify_block_family(header, &parent, parent_uncles, body) + }, None => engine.verify_block_seal(header), } } else { diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index dcb173b63e9..6ca9f746cdf 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -175,6 +175,7 @@ pub fn generate_dummy_client_with_spec_accounts_and_data(get_test_spec: F, ac let kp = KeyPair::from_secret_slice(&"".sha3()).unwrap(); let author = kp.address(); + let parent_uncles = 0; let mut n = 0; for _ in 0..block_number { @@ -187,6 +188,7 @@ pub fn generate_dummy_client_with_spec_accounts_and_data(get_test_spec: F, ac false, db, &last_header, + parent_uncles, Arc::new(last_hashes.clone()), author.clone(), (3141562.into(), 31415620.into()), @@ -438,6 +440,7 @@ pub fn get_default_ethash_params() -> EthashParams{ minimum_difficulty: U256::from(131072), difficulty_bound_divisor: U256::from(2048), difficulty_increment_divisor: 10, + metropolis_difficulty_increment_divisor: 9, duration_limit: 13, block_reward: U256::from(0), registrar: "0000000000000000000000000000000000000001".into(), @@ -448,6 +451,7 @@ pub fn get_default_ethash_params() -> EthashParams{ difficulty_hardfork_transition: u64::max_value(), difficulty_hardfork_bound_divisor: U256::from(0), bomb_defuse_transition: u64::max_value(), + eip100_transition: u64::max_value(), eip150_transition: u64::max_value(), eip155_transition: u64::max_value(), eip160_transition: u64::max_value(), diff --git a/ethcore/src/verification/verification.rs b/ethcore/src/verification/verification.rs index 7fbeb29bb06..5279bad543e 100644 --- a/ethcore/src/verification/verification.rs +++ b/ethcore/src/verification/verification.rs @@ -97,9 +97,10 @@ pub fn verify_block_unordered(header: Header, bytes: Bytes, engine: &Engine, che /// Phase 3 verification. Check block information against parent and uncles. pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error> { // TODO: verify timestamp - let parent = bc.block_header(&header.parent_hash()).ok_or_else(|| Error::from(BlockError::UnknownParent(header.parent_hash().clone())))?; + let parent = bc.block_header(header.parent_hash()).ok_or_else(|| Error::from(BlockError::UnknownParent(header.parent_hash().clone())))?; + let parent_uncles = bc.uncles_count(header.parent_hash()).unwrap_or(0); verify_parent(&header, &parent)?; - engine.verify_block_family(&header, &parent, Some(bytes))?; + engine.verify_block_family(&header, &parent, parent_uncles, Some(bytes))?; let num_uncles = UntrustedRlp::new(bytes).at(2)?.item_count(); if num_uncles != 0 { @@ -171,7 +172,8 @@ pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: & } verify_parent(&uncle, &uncle_parent)?; - engine.verify_block_family(&uncle, &uncle_parent, Some(bytes))?; + let parent_uncles = bc.uncles_count(uncle_parent.parent_hash()).unwrap_or(0); + engine.verify_block_family(&uncle, &uncle_parent, parent_uncles, Some(bytes))?; } } Ok(()) diff --git a/json/src/spec/ethash.rs b/json/src/spec/ethash.rs index 95a70f8a05e..0599258f41c 100644 --- a/json/src/spec/ethash.rs +++ b/json/src/spec/ethash.rs @@ -35,6 +35,9 @@ pub struct EthashParams { #[serde(rename="difficultyIncrementDivisor")] pub difficulty_increment_divisor: Option, /// See main EthashParams docs. + #[serde(rename="metropolisDifficultyIncrementDivisor")] + pub metropolis_difficulty_increment_divisor: Option, + /// See main EthashParams docs. #[serde(rename="durationLimit")] pub duration_limit: Uint, /// See main EthashParams docs. @@ -67,6 +70,10 @@ pub struct EthashParams { #[serde(rename="bombDefuseTransition")] pub bomb_defuse_transition: Option, + /// See main EthashParams docs. + #[serde(rename="eip100Transition")] + pub eip100_transition: Option, + /// See main EthashParams docs. #[serde(rename="eip150Transition")] pub eip150_transition: Option, @@ -164,6 +171,7 @@ mod tests { "difficultyHardforkTransition": "0x59d9", "difficultyHardforkBoundDivisor": "0x0200", "bombDefuseTransition": "0x42", + "eip100Transition": "0x42", "eip150Transition": "0x42", "eip155Transition": "0x42", "eip160Transition": "0x42",