spec: Validate required divisor fields are not 0 (#7933)
* Add validate_non_zero function It's used to validate that a Spec's uint field used as a divisor is not zero. * Add deserialize_with to gas_limit_bound_divisor Prevents panics due to divide-by-zero on the gas_limit_bound_divisor field. * Add deserialize_with to difficulty_bound_divisor Prevents panics due to divide-by-zero on the difficulty_bound_divisor field. * Add validate_optional_non_zero function Used to validate Option<Uint> divisor fields. * Use deserialize_with on optional divisor fields. * Add #[serde(default)] attribute to divisor fields When using `#[serde(deserialize_with)]`, `#[serde(default)]` must be specified so that missing fields can be deserialized with the deserializer for `None`.
This commit is contained in:
parent
76a098114f
commit
e630f647d1
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
//! Ethash params deserialization.
|
//! Ethash params deserialization.
|
||||||
|
|
||||||
use uint::Uint;
|
use uint::{self, Uint};
|
||||||
use hash::Address;
|
use hash::Address;
|
||||||
|
|
||||||
/// Deserializable doppelganger of EthashParams.
|
/// Deserializable doppelganger of EthashParams.
|
||||||
@ -27,12 +27,15 @@ pub struct EthashParams {
|
|||||||
pub minimum_difficulty: Uint,
|
pub minimum_difficulty: Uint,
|
||||||
/// See main EthashParams docs.
|
/// See main EthashParams docs.
|
||||||
#[serde(rename="difficultyBoundDivisor")]
|
#[serde(rename="difficultyBoundDivisor")]
|
||||||
|
#[serde(deserialize_with="uint::validate_non_zero")]
|
||||||
pub difficulty_bound_divisor: Uint,
|
pub difficulty_bound_divisor: Uint,
|
||||||
/// See main EthashParams docs.
|
/// See main EthashParams docs.
|
||||||
#[serde(rename="difficultyIncrementDivisor")]
|
#[serde(rename="difficultyIncrementDivisor")]
|
||||||
|
#[serde(default, deserialize_with="uint::validate_optional_non_zero")]
|
||||||
pub difficulty_increment_divisor: Option<Uint>,
|
pub difficulty_increment_divisor: Option<Uint>,
|
||||||
/// See main EthashParams docs.
|
/// See main EthashParams docs.
|
||||||
#[serde(rename="metropolisDifficultyIncrementDivisor")]
|
#[serde(rename="metropolisDifficultyIncrementDivisor")]
|
||||||
|
#[serde(default, deserialize_with="uint::validate_optional_non_zero")]
|
||||||
pub metropolis_difficulty_increment_divisor: Option<Uint>,
|
pub metropolis_difficulty_increment_divisor: Option<Uint>,
|
||||||
/// See main EthashParams docs.
|
/// See main EthashParams docs.
|
||||||
#[serde(rename="durationLimit")]
|
#[serde(rename="durationLimit")]
|
||||||
@ -60,6 +63,7 @@ pub struct EthashParams {
|
|||||||
pub difficulty_hardfork_transition: Option<Uint>,
|
pub difficulty_hardfork_transition: Option<Uint>,
|
||||||
/// See main EthashParams docs.
|
/// See main EthashParams docs.
|
||||||
#[serde(rename="difficultyHardforkBoundDivisor")]
|
#[serde(rename="difficultyHardforkBoundDivisor")]
|
||||||
|
#[serde(default, deserialize_with="uint::validate_optional_non_zero")]
|
||||||
pub difficulty_hardfork_bound_divisor: Option<Uint>,
|
pub difficulty_hardfork_bound_divisor: Option<Uint>,
|
||||||
/// See main EthashParams docs.
|
/// See main EthashParams docs.
|
||||||
#[serde(rename="bombDefuseTransition")]
|
#[serde(rename="bombDefuseTransition")]
|
||||||
@ -302,4 +306,17 @@ mod tests {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "a non-zero value")]
|
||||||
|
fn test_zero_value_divisor() {
|
||||||
|
let s = r#"{
|
||||||
|
"params": {
|
||||||
|
"difficultyBoundDivisor": "0x0",
|
||||||
|
"minimumDifficulty": "0x020000"
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let _deserialized: Ethash = serde_json::from_str(s).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
//! Spec params deserialization.
|
//! Spec params deserialization.
|
||||||
|
|
||||||
use uint::Uint;
|
use uint::{self, Uint};
|
||||||
use hash::{H256, Address};
|
use hash::{H256, Address};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
@ -102,6 +102,7 @@ pub struct Params {
|
|||||||
pub wasm: Option<bool>,
|
pub wasm: Option<bool>,
|
||||||
/// See `CommonParams` docs.
|
/// See `CommonParams` docs.
|
||||||
#[serde(rename="gasLimitBoundDivisor")]
|
#[serde(rename="gasLimitBoundDivisor")]
|
||||||
|
#[serde(deserialize_with="uint::validate_non_zero")]
|
||||||
pub gas_limit_bound_divisor: Uint,
|
pub gas_limit_bound_divisor: Uint,
|
||||||
/// See `CommonParams` docs.
|
/// See `CommonParams` docs.
|
||||||
pub registrar: Option<Address>,
|
pub registrar: Option<Address>,
|
||||||
@ -149,4 +150,21 @@ mod tests {
|
|||||||
assert_eq!(deserialized.gas_limit_bound_divisor, Uint(U256::from(0x20)));
|
assert_eq!(deserialized.gas_limit_bound_divisor, Uint(U256::from(0x20)));
|
||||||
assert_eq!(deserialized.max_code_size, Some(Uint(U256::from(0x1000))));
|
assert_eq!(deserialized.max_code_size, Some(Uint(U256::from(0x1000))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "a non-zero value")]
|
||||||
|
fn test_zero_value_divisor() {
|
||||||
|
let s = r#"{
|
||||||
|
"maximumExtraDataSize": "0x20",
|
||||||
|
"networkID" : "0x1",
|
||||||
|
"chainID" : "0x15",
|
||||||
|
"subprotocolName" : "exp",
|
||||||
|
"minGasLimit": "0x1388",
|
||||||
|
"accountStartNonce": "0x01",
|
||||||
|
"gasLimitBoundDivisor": "0x0",
|
||||||
|
"maxCodeSize": "0x1000"
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let _deserialized: Params = serde_json::from_str(s).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use serde::de::{Error, Visitor};
|
use serde::de::{Error, Visitor, Unexpected};
|
||||||
use ethereum_types::U256;
|
use ethereum_types::U256;
|
||||||
|
|
||||||
/// Lenient uint json deserialization for test json files.
|
/// Lenient uint json deserialization for test json files.
|
||||||
@ -90,6 +90,28 @@ impl<'a> Visitor<'a> for UintVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn validate_non_zero<'de, D>(d: D) -> Result<Uint, D::Error> where D: Deserializer<'de> {
|
||||||
|
let value = Uint::deserialize(d)?;
|
||||||
|
|
||||||
|
if value == Uint(U256::from(0)) {
|
||||||
|
return Err(Error::invalid_value(Unexpected::Unsigned(value.into()), &"a non-zero value"))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate_optional_non_zero<'de, D>(d: D) -> Result<Option<Uint>, D::Error> where D: Deserializer<'de> {
|
||||||
|
let value: Option<Uint> = Option::deserialize(d)?;
|
||||||
|
|
||||||
|
if let Some(value) = value {
|
||||||
|
if value == Uint(U256::from(0)) {
|
||||||
|
return Err(Error::invalid_value(Unexpected::Unsigned(value.into()), &"a non-zero value"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
Loading…
Reference in New Issue
Block a user