diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index bff11fa2a..e02667d90 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -79,6 +79,14 @@ pub struct EthashParams { pub ecip1010_continue_transition: u64, /// Maximum amount of code that can be deploying into a contract. pub max_code_size: u64, + /// Number of first block where the max gas limit becomes effective. + pub max_gas_limit_transition: u64, + /// Maximum valid block gas limit, + pub max_gas_limit: U256, + /// Number of first block where the minimum gas price becomes effective. + pub min_gas_price_transition: u64, + /// Do not alow transactions with lower gas price. + pub min_gas_price: U256, } impl From for EthashParams { @@ -106,6 +114,10 @@ impl From for EthashParams { ecip1010_pause_transition: p.ecip1010_pause_transition.map_or(u64::max_value(), Into::into), ecip1010_continue_transition: p.ecip1010_continue_transition.map_or(u64::max_value(), Into::into), max_code_size: p.max_code_size.map_or(u64::max_value(), Into::into), + max_gas_limit_transition: p.max_gas_limit_transition.map_or(u64::max_value(), Into::into), + max_gas_limit: p.max_gas_limit.map_or(U256::max_value(), Into::into), + min_gas_price_transition: p.min_gas_price_transition.map_or(u64::max_value(), Into::into), + min_gas_price: p.min_gas_price.map_or(U256::zero(), Into::into), } } } @@ -174,8 +186,12 @@ impl Engine for Ethash { } } - 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, gas_floor_target: U256, mut gas_ceil_target: U256) { let difficulty = self.calculate_difficulty(header, parent); + 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; + } let gas_limit = { let gas_limit = parent.gas_limit().clone(); let bound_divisor = self.ethash_params.gas_limit_bound_divisor; @@ -312,11 +328,15 @@ impl Engine for Ethash { return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() }))) } let gas_limit_divisor = self.ethash_params.gas_limit_bound_divisor; - let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor; - let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor; + let parent_gas_limit = *parent.gas_limit(); + let min_gas = parent_gas_limit - parent_gas_limit / gas_limit_divisor; + let max_gas = parent_gas_limit + parent_gas_limit / gas_limit_divisor; if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas { return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(max_gas), found: header.gas_limit().clone() }))); } + if header.number() >= self.ethash_params.max_gas_limit_transition && header.gas_limit() > &self.ethash_params.max_gas_limit && header.gas_limit() > &parent_gas_limit { + return Err(From::from(BlockError::InvalidGasLimit(OutOfBounds { min: Some(min_gas), max: Some(self.ethash_params.max_gas_limit), found: header.gas_limit().clone() }))); + } Ok(()) } @@ -331,6 +351,10 @@ impl Engine for Ethash { } } + if header.number() >= self.ethash_params.min_gas_price_transition && t.gas_price < self.ethash_params.min_gas_price { + return Err(TransactionError::InsufficientGasPrice { minimal: self.ethash_params.min_gas_price, got: t.gas_price }.into()); + } + Ok(()) } } @@ -853,4 +877,82 @@ mod tests { let difficulty = ethash.calculate_difficulty(&header, &parent_header); assert_eq!(U256::from(12543204905719u64), difficulty); } + + #[test] + fn rejects_blocks_over_max_gas_limit() { + let spec = new_homestead_test(); + let mut ethparams = get_default_ethash_params(); + ethparams.max_gas_limit_transition = 10; + ethparams.max_gas_limit = 100_000.into(); + + let mut parent_header = Header::default(); + parent_header.set_number(1); + parent_header.set_gas_limit(100_000.into()); + let mut header = Header::default(); + header.set_number(parent_header.number() + 1); + 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()); + + 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()); + + 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()); + + 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()); + + 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()); + } + + #[test] + fn rejects_transactions_below_min_gas_price() { + use ethkey::{Generator, Random}; + use types::transaction::{Transaction, Action}; + + let spec = new_homestead_test(); + let mut ethparams = get_default_ethash_params(); + ethparams.min_gas_price_transition = 10; + ethparams.min_gas_price = 100000.into(); + + let mut header = Header::default(); + header.set_number(1); + + let keypair = Random.generate().unwrap(); + let tx1 = Transaction { + action: Action::Create, + value: U256::zero(), + data: Vec::new(), + gas: 100_000.into(), + gas_price: 100_000.into(), + nonce: U256::zero(), + }.sign(keypair.secret(), None).into(); + + let tx2 = Transaction { + action: Action::Create, + value: U256::zero(), + data: Vec::new(), + gas: 100_000.into(), + gas_price: 99_999.into(), + nonce: U256::zero(), + }.sign(keypair.secret(), None).into(); + + let ethash = Ethash::new(spec.params, ethparams, BTreeMap::new()); + assert!(ethash.verify_transaction_basic(&tx1, &header).is_ok()); + assert!(ethash.verify_transaction_basic(&tx2, &header).is_ok()); + + header.set_number(10); + assert!(ethash.verify_transaction_basic(&tx1, &header).is_ok()); + assert!(ethash.verify_transaction_basic(&tx2, &header).is_err()); + } } diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index a937f4cf9..92feb96cf 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -456,5 +456,9 @@ pub fn get_default_ethash_params() -> EthashParams{ ecip1010_pause_transition: u64::max_value(), ecip1010_continue_transition: u64::max_value(), max_code_size: u64::max_value(), + max_gas_limit_transition: u64::max_value(), + max_gas_limit: U256::max_value(), + min_gas_price_transition: u64::max_value(), + min_gas_price: U256::zero(), } } diff --git a/json/src/spec/ethash.rs b/json/src/spec/ethash.rs index 4cea4926a..95a70f8a0 100644 --- a/json/src/spec/ethash.rs +++ b/json/src/spec/ethash.rs @@ -97,6 +97,21 @@ pub struct EthashParams { #[serde(rename="maxCodeSize")] pub max_code_size: Option, + /// See main EthashParams docs. + #[serde(rename="maxGasLimitTransition")] + pub max_gas_limit_transition: Option, + + /// See main EthashParams docs. + #[serde(rename="maxGasLimit")] + pub max_gas_limit: Option, + + /// See main EthashParams docs. + #[serde(rename="minGasPriceTransition")] + pub min_gas_price_transition: Option, + + /// See main EthashParams docs. + #[serde(rename="minGasPrice")] + pub min_gas_price: Option, } /// Ethash engine deserialization.