Make conversions safer.

This commit is contained in:
chriseth
2024-01-25 09:58:40 +01:00
parent d457b2e0cd
commit fda565e703
6 changed files with 48 additions and 16 deletions

View File

@@ -253,6 +253,18 @@ macro_rules! powdr_field {
}
}
impl FromStr for $name {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let n = BigUint::from_str(s).map_err(|e| e.to_string())?;
if n >= <$ark_type>::MODULUS.into() {
Err(format!("Decimal number \"{s}\" too large for field."))
} else {
Ok(n.into())
}
}
}
impl fmt::LowerHex for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::LowerHex::fmt(&self.to_integer(), f)
@@ -267,16 +279,13 @@ macro_rules! powdr_field {
Some(KnownField::$name)
}
fn from_str(s: &str) -> Self {
Self {
value: <$ark_type>::from_str(s).unwrap(),
}
}
fn from_str_radix(s: &str, radix: u32) -> Result<Self, String> {
BigUint::from_str_radix(s, radix)
.map(|n| n.into())
.map_err(|e| e.to_string())
let n = BigUint::from_str_radix(s, radix).map_err(|e| e.to_string())?;
if n >= <$ark_type>::MODULUS.into() {
Err(format!("Hexadecimal number \"0x{s}\" too large for field."))
} else {
Ok(n.into())
}
}
fn to_degree(&self) -> DegreeType {

View File

@@ -68,9 +68,9 @@ pub fn read_polys_csv_file<T: FieldElement>(file: &mut impl Read) -> Vec<(String
let value = if let Some(value) = value.strip_prefix("0x") {
T::from_str_radix(value, 16).unwrap()
} else if let Some(value) = value.strip_prefix('-') {
-T::from_str(value)
-T::from_str(value).unwrap()
} else {
T::from_str(value)
T::from_str(value).unwrap()
};
polys[idx].1.push(value);
}

View File

@@ -1,4 +1,4 @@
use std::{fmt, hash::Hash, ops::*};
use std::{fmt, hash::Hash, ops::*, str::FromStr};
use num_traits::{One, Zero};
@@ -78,6 +78,7 @@ pub trait FieldElement:
+ fmt::Debug
+ From<Self::Integer>
+ From<num_bigint::BigUint>
+ FromStr<Err = String>
+ From<u32>
+ From<u64>
+ From<i32>
@@ -110,8 +111,6 @@ pub trait FieldElement:
fn from_bytes_le(bytes: &[u8]) -> Self;
fn from_str(s: &str) -> Self;
fn from_str_radix(s: &str, radix: u32) -> Result<Self, String>;
/// Returns true if the value is in the "lower half" of the field,

View File

@@ -576,7 +576,7 @@ PublicIdentifier: String = {
}
FieldElement: T = {
r"[0-9][0-9_]*" => T::from_str(&<>.replace('_', "")),
r"[0-9][0-9_]*" => T::from_str(&<>.replace('_', "")).unwrap(),
r"0x[0-9A-Fa-f][0-9A-Fa-f_]*" => T::from_str_radix(&<>[2..].replace('_', ""), 16).unwrap(),
}

View File

@@ -591,4 +591,28 @@ mod test {
"#;
parse_and_evaluate_symbol(src, "F.x");
}
#[test]
#[should_panic = r#"Hexadecimal number \"0x9999999999999999999999999999999\" too large for field"#]
pub fn hex_number_outside_field() {
// This tests that the parser does not lose precision when parsing large integers.
// We are currently going through FieldElements in the parser, which have limited precision.
// As soon as we use Integers there, this test should succeed.
let src = r#"
let N = 0x9999999999999999999999999999999;
"#;
parse_and_evaluate_symbol(src, "N");
}
#[test]
#[should_panic = r#"Decimal number \"9999999999999999999999999999999\" too large for field"#]
pub fn decimal_number_outside_field() {
// This tests that the parser does not lose precision when parsing large integers.
// We are currently going through FieldElements in the parser, which have limited precision.
// As soon as we use Integers there, this test should succeed.
let src = r#"
let N = 9999999999999999999999999999999;
"#;
parse_and_evaluate_symbol(src, "N");
}
}

View File

@@ -99,7 +99,7 @@ pub fn inputs_to_query_callback<T: FieldElement>(inputs: Vec<T>) -> impl QueryCa
// called again.
Ok(Some(0.into()))
}
["\"hint\"", value] => Ok(Some(T::from_str(value))),
["\"hint\"", value] => Ok(Some(T::from_str(value).unwrap())),
k => Err(format!("Unsupported query: {}", k.iter().format(", "))),
}
}