From 808cd745e4daf033a0573cf9688dddde594b6445 Mon Sep 17 00:00:00 2001 From: librelois <elois@ifee.fr> Date: Sun, 2 Jun 2019 18:45:42 +0200 Subject: [PATCH] [fix] json-parser: very large integers were not parsed correctly --- lib/core/core/src/commands/reset.rs | 4 +- .../blockchain/blockchain/src/sync/mod.rs | 4 +- .../ws2p-v1-legacy/src/parsers/blocks.rs | 2 +- lib/tools/documents/src/documents/block.rs | 14 ++- lib/tools/documents/src/parsers/blocks.rs | 2 +- lib/tools/json-pest-parser/src/lib.rs | 88 +++++++++++++++++-- 6 files changed, 98 insertions(+), 16 deletions(-) diff --git a/lib/core/core/src/commands/reset.rs b/lib/core/core/src/commands/reset.rs index 855adb83..d71a449f 100644 --- a/lib/core/core/src/commands/reset.rs +++ b/lib/core/core/src/commands/reset.rs @@ -61,13 +61,13 @@ impl DursExecutableCoreCommand for ResetOpt { match self.reset_type { ResetType::Datas => { let mut currency_datas_path = profile_path; - currency_datas_path.push("g1"); + currency_datas_path.push(durs_conf::constants::MODULES_DATAS_FOLDER); fs::remove_dir_all(currency_datas_path.as_path()) .map_err(DursCoreError::FailRemoveDatasDir) } ResetType::Conf => { let mut conf_file_path = profile_path.clone(); - conf_file_path.push("conf.json"); + conf_file_path.push(durs_conf::constants::CONF_FILENAME); fs::remove_file(conf_file_path.as_path()).map_err(DursCoreError::FailRemoveConfFile) } ResetType::All => fs::remove_dir_all(profile_path.as_path()) diff --git a/lib/modules/blockchain/blockchain/src/sync/mod.rs b/lib/modules/blockchain/blockchain/src/sync/mod.rs index 131dcbf6..33a12537 100644 --- a/lib/modules/blockchain/blockchain/src/sync/mod.rs +++ b/lib/modules/blockchain/blockchain/src/sync/mod.rs @@ -92,7 +92,7 @@ pub fn local_sync<DC: DursConfTrait>(profile_path: PathBuf, conf: &DC, sync_opts currency, end, cautious_mode: cautious, - unsafe_mode: verif_inner_hash, + unsafe_mode, .. } = sync_opts; @@ -265,7 +265,7 @@ pub fn local_sync<DC: DursConfTrait>(profile_path: PathBuf, conf: &DC, sync_opts // Verify block hashs let verif_block_hashs_begin = SystemTime::now(); - if verif_inner_hash { + if !unsafe_mode { dubp::check::hashs::verify_block_hashs(&block_doc) .expect("Receive wrong block, please reset data and resync !"); } diff --git a/lib/modules/ws2p-v1-legacy/src/parsers/blocks.rs b/lib/modules/ws2p-v1-legacy/src/parsers/blocks.rs index 75143b30..57767c2f 100644 --- a/lib/modules/ws2p-v1-legacy/src/parsers/blocks.rs +++ b/lib/modules/ws2p-v1-legacy/src/parsers/blocks.rs @@ -119,7 +119,7 @@ pub fn parse_json_block(source: &serde_json::Value) -> Option<BlockDocument> { )?))); } Some(BlockDocument { - nonce: source.get("nonce")?.as_i64()? as u64, + nonce: source.get("nonce")?.as_u64()? as u64, version: source.get("version")?.as_u64()? as u32, number: BlockNumber(source.get("number")?.as_u64()? as u32), pow_min: source.get("powMin")?.as_u64()? as usize, diff --git a/lib/tools/documents/src/documents/block.rs b/lib/tools/documents/src/documents/block.rs index eed6d4eb..3a3409c9 100644 --- a/lib/tools/documents/src/documents/block.rs +++ b/lib/tools/documents/src/documents/block.rs @@ -172,11 +172,21 @@ impl BlockDocument { pub fn verify_hash(&self) -> bool { match self.hash { Some(hash) => { - hash == BlockHash(Hash::compute_str(&format!( + let datas = format!( "{}{}\n", self.generate_will_hashed_string(), self.signatures[0] - ))) + ); + let expected_hash = BlockHash(Hash::compute_str(&datas)); + if hash == expected_hash { + true + } else { + warn!( + "Block #{} have invalid hash (expected='{}', actual='{}', datas='{}').", + self.number.0, expected_hash, hash, datas + ); + false + } } None => false, } diff --git a/lib/tools/documents/src/parsers/blocks.rs b/lib/tools/documents/src/parsers/blocks.rs index fb0c275b..d155cfb9 100644 --- a/lib/tools/documents/src/parsers/blocks.rs +++ b/lib/tools/documents/src/parsers/blocks.rs @@ -42,7 +42,7 @@ pub fn parse_json_block(json_block: &JSONValue<DefaultHasher>) -> Result<BlockDo Ok(BlockDocument { version: get_number(json_block, "version")?.trunc() as u32, - nonce: get_number(json_block, "nonce")?.trunc() as u64, + nonce: get_u64(json_block, "nonce")?, number: BlockNumber(block_number), pow_min: get_number(json_block, "powMin")?.trunc() as usize, time: get_number(json_block, "time")?.trunc() as u64, diff --git a/lib/tools/json-pest-parser/src/lib.rs b/lib/tools/json-pest-parser/src/lib.rs index 82f971f0..05ae3c23 100644 --- a/lib/tools/json-pest-parser/src/lib.rs +++ b/lib/tools/json-pest-parser/src/lib.rs @@ -40,6 +40,7 @@ use failure::Error; use pest::iterators::Pair; use pest::Parser; use std::collections::HashMap; +use std::str::FromStr; #[derive(Parser)] #[grammar = "json_grammar.pest"] @@ -50,11 +51,17 @@ pub enum JSONValue<'a, S: std::hash::BuildHasher> { Object(HashMap<&'a str, JSONValue<'a, S>, S>), Array(Vec<JSONValue<'a, S>>), String(&'a str), - Number(f64), + Number(Number), Boolean(bool), Null, } +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Number { + F64(f64), + U64(u64), +} + type JsonObject<'a, S> = HashMap<&'a str, JSONValue<'a, S>, S>; impl<'a, S: std::hash::BuildHasher> JSONValue<'a, S> { @@ -114,9 +121,24 @@ impl<'a, S: std::hash::BuildHasher> JSONValue<'a, S> { } } - pub fn to_number(&self) -> Option<f64> { + pub fn to_f64(&self) -> Option<f64> { + if let JSONValue::Number(number) = self { + match number { + Number::F64(f64_) => Some(*f64_), + Number::U64(u64_) => Some(*u64_ as f64), + } + } else { + None + } + } + + pub fn to_u64(&self) -> Option<u64> { if let JSONValue::Number(number) = self { - Some(*number) + if let Number::U64(u64_) = number { + Some(*u64_) + } else { + None + } } else { None } @@ -162,7 +184,10 @@ impl<'a, S: std::hash::BuildHasher> ToString for JSONValue<'a, S> { format!("[{}]", contents.join(",")) } JSONValue::String(s) => format!("\"{}\"", s), - JSONValue::Number(n) => format!("{}", n), + JSONValue::Number(n) => match n { + Number::F64(f64_) => format!("{}", f64_), + Number::U64(u64_) => format!("{}", u64_), + }, JSONValue::Boolean(b) => format!("{}", b), JSONValue::Null => "null".to_owned(), } @@ -217,7 +242,13 @@ fn parse_value<S: std::hash::BuildHasher + Default>(pair: Pair<Rule>) -> JSONVal ), Rule::array => JSONValue::Array(pair.into_inner().map(parse_value).collect()), Rule::string => JSONValue::String(pair.into_inner().next().unwrap().as_str()), - Rule::number => JSONValue::Number(pair.as_str().parse().unwrap()), + Rule::number => { + if let Ok(number_u64) = u64::from_str(pair.as_str()) { + JSONValue::Number(Number::U64(number_u64)) + } else { + JSONValue::Number(Number::F64(pair.as_str().parse().unwrap())) + } + } Rule::boolean => JSONValue::Boolean(pair.as_str().parse().unwrap()), Rule::null => JSONValue::Null, Rule::json @@ -239,7 +270,7 @@ pub fn get_optional_usize<S: std::hash::BuildHasher>( if !value.is_null() { Some( value - .to_number() + .to_f64() .ok_or_else(|| ParseJsonError { cause: format!( "Fail to parse json : field '{}' must be a number !", @@ -290,6 +321,21 @@ pub fn get_optional_str<'a, S: std::hash::BuildHasher>( }) } +pub fn get_u64<S: std::hash::BuildHasher>( + json_block: &HashMap<&str, JSONValue<S>, S>, + field: &str, +) -> Result<u64, Error> { + Ok(json_block + .get(field) + .ok_or_else(|| ParseJsonError { + cause: format!("Fail to parse json : field '{}' must exist !", field), + })? + .to_u64() + .ok_or_else(|| ParseJsonError { + cause: format!("Fail to parse json : field '{}' must be a number !", field), + })?) +} + pub fn get_number<S: std::hash::BuildHasher>( json_block: &HashMap<&str, JSONValue<S>, S>, field: &str, @@ -299,7 +345,7 @@ pub fn get_number<S: std::hash::BuildHasher>( .ok_or_else(|| ParseJsonError { cause: format!("Fail to parse json : field '{}' must exist !", field), })? - .to_number() + .to_f64() .ok_or_else(|| ParseJsonError { cause: format!("Fail to parse json : field '{}' must be a number !", field), })?) @@ -371,6 +417,29 @@ pub fn get_object_array<'a, S: std::hash::BuildHasher>( mod tests { use super::*; + #[test] + fn test_parse_too_large_number() { + assert_eq!( + Ok(100_010_200_000_006_940), + u64::from_str("100010200000006940"), + ); + + let json_string = "{ + \"nonce\": 100010200000006940 + }"; + + let json_value = parse_json_string(json_string).expect("Fail to parse json string !"); + + assert!(json_value.is_object()); + + let json_object = json_value.to_object().expect("safe unwrap"); + + assert_eq!( + json_object.get("nonce"), + Some(&JSONValue::Number(Number::U64(100_010_200_000_006_940))) + ); + } + #[test] fn test_parse_json_string() { let json_string = "{ @@ -389,7 +458,10 @@ mod tests { let json_object = json_value.to_object().expect("safe unwrap"); assert_eq!(json_object.get("name"), Some(&JSONValue::String("toto"))); - assert_eq!(json_object.get("age"), Some(&JSONValue::Number(25f64))); + assert_eq!( + json_object.get("age"), + Some(&JSONValue::Number(Number::U64(25u64))) + ); let friends = json_object .get("friends") -- GitLab