Skip to content

Commit c9d02b5

Browse files
jotabulaciosNicolenicole-grausdiegokingston
authored
Add big uint conversion (#1002)
* first iteration to add BigUint conversion * remove comments * refactor * refactor * save work. tests working for different fields * small refactor * conversion not working for baby bear u32 * all tests working for every field * remove comments in u64_prime_field file * remove commented code * fix cargo check no-default-features * remove changes in unsigned_integer * fix ensure-no-std * remove Ok and unwrap * fix ci set up job * fix compile error * add BN254 conversion test * change test for bn254 --------- Co-authored-by: Nicole <[email protected]> Co-authored-by: Nicole <[email protected]> Co-authored-by: Diego K <[email protected]>
1 parent 5f8f2cf commit c9d02b5

File tree

5 files changed

+230
-9
lines changed

5 files changed

+230
-9
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
runs-on: ubuntu-latest
1717
steps:
1818
- name: Checkout sources
19-
uses: actions/checkout@v3
19+
uses: actions/checkout@v4
2020

2121
- name: Rustup toolchain install
2222
uses: dtolnay/rust-toolchain@stable

crates/math/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ rayon = { version = "1.7", optional = true }
2626
cudarc = { version = "0.9.7", optional = true }
2727

2828
lambdaworks-gpu = { workspace = true, optional = true }
29+
num-bigint = { version = "0.4.6", default-features = false }
30+
num-traits = { version = "0.2.19", default-features = false }
2931

3032
[dev-dependencies]
3133
rand_chacha = "0.3.1"

crates/math/src/errors.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub enum ByteConversionError {
55
InvalidValue,
66
PointNotInSubgroup,
77
ValueNotCompressed,
8+
ValueNotReduced,
89
}
910

1011
#[derive(Debug, PartialEq, Eq)]

crates/math/src/field/element.rs

Lines changed: 224 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
use crate::errors::CreationError;
1+
use crate::errors::{ByteConversionError, CreationError};
22
use crate::field::errors::FieldError;
33
use crate::field::traits::IsField;
4-
#[cfg(feature = "lambdaworks-serde-binary")]
54
use crate::traits::ByteConversion;
65
use crate::unsigned_integer::element::UnsignedInteger;
76
use crate::unsigned_integer::montgomery::MontgomeryAlgorithms;
87
use crate::unsigned_integer::traits::IsUnsignedInteger;
8+
#[cfg(feature = "alloc")]
9+
use alloc::{
10+
format,
11+
string::{String, ToString},
12+
};
913
use core::fmt;
1014
use core::fmt::Debug;
1115
use core::iter::Sum;
@@ -15,6 +19,8 @@ use core::iter::Sum;
1519
))]
1620
use core::marker::PhantomData;
1721
use core::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub};
22+
use num_bigint::BigUint;
23+
use num_traits::Num;
1824
#[cfg(any(
1925
feature = "lambdaworks-serde-binary",
2026
feature = "lambdaworks-serde-string"
@@ -104,6 +110,21 @@ where
104110
}
105111
}
106112

113+
#[cfg(feature = "alloc")]
114+
/// From overloading for BigUint.
115+
/// Creates a field element from a BigUint that is smaller than the modulus.
116+
/// Returns error if the BigUint value is bigger than the modulus.
117+
impl<F> TryFrom<BigUint> for FieldElement<F>
118+
where
119+
Self: ByteConversion,
120+
F: IsPrimeField,
121+
{
122+
type Error = ByteConversionError;
123+
fn try_from(value: BigUint) -> Result<Self, ByteConversionError> {
124+
FieldElement::<F>::from_reduced_big_uint(&value)
125+
}
126+
}
127+
107128
impl<F> FieldElement<F>
108129
where
109130
F::BaseType: Clone,
@@ -491,6 +512,77 @@ where
491512
value: <F as IsSubFieldOf<L>>::embed(self.value),
492513
}
493514
}
515+
516+
#[cfg(feature = "alloc")]
517+
/// Creates a field element from a BigUint that is smaller than the modulus.
518+
/// Returns error if the value is bigger than the modulus.
519+
pub fn from_reduced_big_uint(value: &BigUint) -> Result<Self, ByteConversionError>
520+
where
521+
Self: ByteConversion,
522+
F: IsPrimeField,
523+
{
524+
let mod_minus_one = F::modulus_minus_one().to_string();
525+
526+
// We check if `mod_minus_one` is a hex string or a decimal string.
527+
// In case it is a hex we remove the prefix `0x`.
528+
let (digits, radix) = if let Some(hex) = mod_minus_one
529+
.strip_prefix("0x")
530+
.or_else(|| mod_minus_one.strip_prefix("0X"))
531+
{
532+
(hex, 16)
533+
} else {
534+
(mod_minus_one.as_str(), 10)
535+
};
536+
537+
let modulus =
538+
BigUint::from_str_radix(digits, radix).expect("invalid modulus representation") + 1u32;
539+
540+
if value >= &modulus {
541+
Err(ByteConversionError::ValueNotReduced)
542+
} else {
543+
let mut bytes = value.to_bytes_le();
544+
// We pad the bytes to the size of the base type to be able to apply `from_bytes_le`.
545+
bytes.resize(core::mem::size_of::<F::BaseType>(), 0);
546+
Self::from_bytes_le(&bytes)
547+
}
548+
}
549+
550+
#[cfg(feature = "alloc")]
551+
/// Converts a field element into a BigUint.
552+
pub fn to_big_uint(&self) -> BigUint
553+
where
554+
Self: ByteConversion,
555+
{
556+
BigUint::from_bytes_be(&self.to_bytes_be())
557+
}
558+
559+
#[cfg(feature = "alloc")]
560+
/// Converts a hex string into a field element.
561+
/// It returns error if the hex value is larger than the modulus.
562+
pub fn from_hex_str(hex: &str) -> Result<Self, CreationError>
563+
where
564+
Self: ByteConversion,
565+
F: IsPrimeField,
566+
{
567+
let hex_str = hex.strip_prefix("0x").unwrap_or(hex);
568+
if hex_str.is_empty() {
569+
return Err(CreationError::EmptyString);
570+
}
571+
572+
let value =
573+
BigUint::from_str_radix(hex_str, 16).map_err(|_| CreationError::InvalidHexString)?;
574+
575+
Self::from_reduced_big_uint(&value).map_err(|_| CreationError::InvalidHexString)
576+
}
577+
578+
#[cfg(feature = "alloc")]
579+
/// Converts a field element into a hex string.
580+
pub fn to_hex_str(&self) -> String
581+
where
582+
Self: ByteConversion,
583+
{
584+
format!("0x{:02X}", self.to_big_uint())
585+
}
494586
}
495587

496588
impl<F: IsPrimeField> FieldElement<F> {
@@ -514,8 +606,8 @@ impl<F: IsPrimeField> FieldElement<F> {
514606
/// Creates a `FieldElement` from a hexstring. It can contain `0x` or not.
515607
/// Returns an `CreationError::InvalidHexString`if the value is not a hexstring.
516608
/// Returns a `CreationError::EmptyString` if the input string is empty.
517-
/// Returns a `CreationError::HexStringIsTooBig` if the the input hex string is bigger
518-
/// than the maximum amount of characters for this element.
609+
/// Returns a `CreationError::HexStringIsTooBig` if the the input hex string is bigger than the
610+
/// maximum amount of characters for this element.
519611
pub fn from_hex(hex_string: &str) -> Result<Self, CreationError> {
520612
if hex_string.is_empty() {
521613
return Err(CreationError::EmptyString);
@@ -728,14 +820,20 @@ where
728820

729821
#[cfg(test)]
730822
mod tests {
731-
use crate::field::element::FieldElement;
732-
use crate::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField;
823+
use super::*;
824+
use crate::elliptic_curve::short_weierstrass::curves::bn_254::field_extension::BN254PrimeField;
825+
use crate::field::fields::fft_friendly::{
826+
babybear_u32::Babybear31PrimeField, stark_252_prime_field::Stark252PrimeField,
827+
};
828+
use crate::field::fields::montgomery_backed_prime_fields::U384PrimeField;
733829
use crate::field::fields::u64_prime_field::U64PrimeField;
734830
use crate::field::test_fields::u64_test_field::U64TestField;
735831
#[cfg(feature = "alloc")]
736832
use crate::unsigned_integer::element::UnsignedInteger;
833+
use crate::unsigned_integer::element::U384;
737834
#[cfg(feature = "alloc")]
738835
use alloc::vec::Vec;
836+
use num_bigint::BigUint;
739837
#[cfg(feature = "alloc")]
740838
use proptest::collection;
741839
use proptest::{prelude::*, prop_compose, proptest, strategy::Strategy};
@@ -910,4 +1008,124 @@ mod tests {
9101008
}
9111009
}
9121010
}
1011+
1012+
// Tests for BigUint conversion.
1013+
// We define different fields to test the conversion.
1014+
1015+
// Prime field with modulus 17 and base type u64.
1016+
type U64F17 = U64PrimeField<17>;
1017+
type U64F17Element = FieldElement<U64F17>;
1018+
1019+
// Baby Bear Prime field with u32 montgomery backend.
1020+
type BabyBear = Babybear31PrimeField;
1021+
type BabyBearElement = FieldElement<BabyBear>;
1022+
1023+
// Prime field with modulus 23, using u64 montgomery backend of 6 limbs.
1024+
#[derive(Clone, Debug)]
1025+
struct U384Modulus23;
1026+
impl IsModulus<U384> for U384Modulus23 {
1027+
const MODULUS: U384 = UnsignedInteger::from_u64(23);
1028+
}
1029+
type U384F23 = U384PrimeField<U384Modulus23>;
1030+
type U384F23Element = FieldElement<U384F23>;
1031+
1032+
#[test]
1033+
fn test_reduced_biguint_conversion_u64_field() {
1034+
let value = BigUint::from(10u32);
1035+
let fe = U64F17Element::try_from(value.clone()).unwrap();
1036+
let back_to_biguint = fe.to_big_uint();
1037+
assert_eq!(value, back_to_biguint);
1038+
}
1039+
1040+
#[test]
1041+
fn test_reduced_biguint_conversion_baby_bear() {
1042+
let value = BigUint::from(1000u32);
1043+
let fe = BabyBearElement::from_reduced_big_uint(&value).unwrap();
1044+
assert_eq!(fe, BabyBearElement::from(1000));
1045+
let back_to_biguint = fe.to_big_uint();
1046+
assert_eq!(value, back_to_biguint);
1047+
}
1048+
1049+
#[test]
1050+
fn test_reduced_biguint_conversion_u384_field() {
1051+
let value = BigUint::from(22u32);
1052+
let fe = U384F23Element::from_reduced_big_uint(&value).unwrap();
1053+
let back_to_biguint = fe.to_big_uint();
1054+
assert_eq!(value, back_to_biguint);
1055+
}
1056+
#[test]
1057+
fn test_bn254_field_biguint_conversion() {
1058+
type BN254Element = FieldElement<BN254PrimeField>;
1059+
let value = BigUint::from(1001u32);
1060+
let fe = BN254Element::from_reduced_big_uint(&value).unwrap();
1061+
let back_to_biguint = fe.to_big_uint();
1062+
assert_eq!(value, back_to_biguint);
1063+
}
1064+
1065+
#[test]
1066+
fn non_reduced_biguint_value_conversion_errors_u64_field() {
1067+
let value = BigUint::from(17u32);
1068+
let result = U64F17Element::from_reduced_big_uint(&value);
1069+
assert_eq!(result, Err(ByteConversionError::ValueNotReduced));
1070+
}
1071+
1072+
#[test]
1073+
fn non_reduced_biguint_value_conversion_errors_baby_bear() {
1074+
let value = BigUint::from(2013265921u32);
1075+
let result = BabyBearElement::try_from(value);
1076+
assert_eq!(result, Err(ByteConversionError::ValueNotReduced));
1077+
}
1078+
1079+
#[test]
1080+
fn non_reduced_biguint_value_conversion_errors_u384_field() {
1081+
let value = BigUint::from(30u32);
1082+
let result = U384F23Element::try_from(value);
1083+
assert_eq!(result, Err(ByteConversionError::ValueNotReduced));
1084+
}
1085+
1086+
#[test]
1087+
fn test_hex_string_conversion_u64_field() {
1088+
let hex_str = "0x0a";
1089+
let fe = U64F17Element::from_hex_str(hex_str).unwrap();
1090+
assert_eq!(fe, U64F17Element::from(10));
1091+
assert_eq!(fe.to_hex_str(), "0x0A");
1092+
}
1093+
1094+
#[test]
1095+
fn test_hex_string_conversion_baby_bear() {
1096+
let hex_str = "0x77FFFFFF"; // 2013265919
1097+
let fe = BabyBearElement::from_hex_str(hex_str).unwrap();
1098+
assert_eq!(fe, BabyBearElement::from(2013265919));
1099+
assert_eq!(fe.to_hex_str(), "0x77FFFFFF");
1100+
}
1101+
1102+
#[test]
1103+
fn test_hex_string_conversion_u384_field() {
1104+
let hex_str = "0x14"; // 20
1105+
let fe = U384F23Element::from_hex_str(hex_str).unwrap();
1106+
assert_eq!(fe, U384F23Element::from(20));
1107+
assert_eq!(fe.to_hex_str(), "0x14");
1108+
}
1109+
1110+
#[test]
1111+
fn test_invalid_hex_string_u64_field() {
1112+
let hex_str = "0xzz";
1113+
let result = U64F17Element::from_hex_str(hex_str);
1114+
assert!(result.is_err());
1115+
}
1116+
1117+
#[test]
1118+
fn test_invalid_hex_string_baby_bear() {
1119+
// modulus = 0x78000001
1120+
let hex_str = "0x78000001";
1121+
let result = BabyBearElement::from_hex_str(hex_str);
1122+
assert!(result.is_err());
1123+
}
1124+
1125+
#[test]
1126+
fn test_empty_hex_string() {
1127+
let hex_str = "";
1128+
let result = U64F17Element::from_hex_str(hex_str);
1129+
assert!(result.is_err());
1130+
}
9131131
}

crates/math/src/field/fields/montgomery_backed_prime_fields.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,8 @@ where
295295

296296
fn field_bit_size() -> usize {
297297
let mut evaluated_bit = NUM_LIMBS * 64 - 1;
298-
let max_element = M::MODULUS - UnsignedInteger::<NUM_LIMBS>::from_u128(1);
299-
let one = UnsignedInteger::from_u128(1);
298+
let max_element = M::MODULUS - UnsignedInteger::<NUM_LIMBS>::from_u64(1);
299+
let one = UnsignedInteger::from_u64(1);
300300

301301
while ((max_element >> evaluated_bit) & one) != one {
302302
evaluated_bit -= 1;

0 commit comments

Comments
 (0)