From 453eb12debe934a798a2c17630b1d25cd353068c Mon Sep 17 00:00:00 2001 From: Balazs Komuves Date: Fri, 23 Jan 2026 19:37:04 +0100 Subject: [PATCH] add conversion to/from bytes --- README.md | 2 +- src/bin/testmain.rs | 20 ++++++++++++++ src/bn254/bigint.rs | 58 +++++++++++++++++++++++++++++++++++++++++ src/bn254/field.rs | 26 ++++++++++++++++++ src/bn254/montgomery.rs | 28 ++++++++++++++++++++ 5 files changed, 133 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8049ea7..9f50a39 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,6 @@ Currently, only Poseidon2 with `t=3` is implemented. - [ ] benchmark RISC-V cycles - [ ] add more Poseidon2 state widths (not just `t=3`) - [ ] implement `circomlib`-compatible Poseidon +- [ ] add a test-suite; in particular, more complete testing of the field operations - [ ] add a 64 bit version -- [ ] more complete testing of the field operations - [ ] further optimizations diff --git a/src/bin/testmain.rs b/src/bin/testmain.rs index 7a524d3..8041530 100644 --- a/src/bin/testmain.rs +++ b/src/bin/testmain.rs @@ -163,6 +163,26 @@ fn main() { BigInt::is_lt_prime(&c) ); */ + { + println!(""); + println!("conversion to/from bytes"); + let a = FELT1; + let xs = Felt::to_le_bytes(&a); + let b = Felt::unsafe_from_le_bytes(&xs); + println!("a = {}",a); + println!("b = {}",b); + println!("le = {:?}",xs); + } + + { + let a = FELT2; + let ys = Felt::to_be_bytes(&a); + let b = Felt::unsafe_from_be_bytes(&ys); + println!("a = {}",a); + println!("b = {}",b); + println!("be = {:?}",ys); + } + //---------------------------------------------------------------------------- } diff --git a/src/bn254/bigint.rs b/src/bn254/bigint.rs index 522b63a..096a1e7 100644 --- a/src/bn254/bigint.rs +++ b/src/bn254/bigint.rs @@ -61,6 +61,59 @@ impl BigInt { BigInt(ls) } + //------------------------------------ + // conversion to/from bytes + + pub fn to_le_bytes(big: &BigInt) -> [u8; 4*N] { + let mut buf : [u8; 4*N] = [0; 4*N]; + for i in 0..N { + let xs: [u8; 4] = big.0[i].to_le_bytes(); + let k = 4*i; + for j in 0..4 { + buf[k + j] = xs[j]; + } + } + buf + } + + pub fn from_le_bytes(buf : &[u8; 4*N]) -> BigInt { + let mut ws: [u32; N] = [0; N]; + for i in 0..N { + let k = 4*i; + let mut xs: [u8; 4] = [0; 4]; + for j in 0..4 { xs[j] = buf[k+j]; } // stupid rust... + let w: u32 = u32::from_le_bytes(xs); + ws[i] = w; + } + BigInt(ws) + } + + pub fn to_be_bytes(big: &BigInt) -> [u8; 4*N] { + let mut buf : [u8; 4*N] = [0; 4*N]; + for i in 0..N { + let xs: [u8; 4] = big.0[N-1-i].to_be_bytes(); + let k = 4*i; + for j in 0..4 { + buf[k + j] = xs[j]; + } + } + buf + } + + pub fn from_be_bytes(buf : &[u8; 4*N]) -> BigInt { + let mut ws: [u32; N] = [0; N]; + for i in 0..N { + let k = 4*i; + let mut xs: [u8; 4] = [0; 4]; + for j in 0..4 { xs[j] = buf[k+j]; } // stupid rust... + let w: u32 = u32::from_be_bytes(xs); + ws[N-1-i] = w; + } + BigInt(ws) + } + + //------------------------------------ + pub fn truncate1(big : &BigInt<{N+1}>) -> BigInt { // let small: [u32; N] = &big.limbs[0..N]; let mut small: [u32; N] = [0; N]; @@ -78,6 +131,9 @@ impl BigInt { BigInt(xs) } + //------------------------------------ + // comparison + pub fn is_zero(big: &BigInt) -> bool { let mut ok : bool = true; for i in 0..N { @@ -132,6 +188,7 @@ impl BigInt { } //------------------------------------ + // addition and subtraction #[inline(always)] #[unroll_for_loops] @@ -235,6 +292,7 @@ impl BigInt { } //------------------------------------ + // multiplication pub fn scale(scalar: u32, big2: &BigInt) -> (BigInt, u32) { let mut c : u32 = 0; diff --git a/src/bn254/field.rs b/src/bn254/field.rs index d8797ec..fe4d0cd 100644 --- a/src/bn254/field.rs +++ b/src/bn254/field.rs @@ -49,6 +49,10 @@ impl Felt { Felt(BigInt::make(xs)) } + pub fn is_valid(felt: &Felt) -> bool { + BigInt::is_lt_prime(&felt.0) + } + pub fn checked_make( xs: [u32; 8] ) -> Felt { let big: Big = BigInt::make(xs); if BigInt::is_lt_prime(&big) { @@ -59,6 +63,26 @@ impl Felt { } } + //------------------------------------ + + pub fn to_le_bytes(felt: &Felt) -> [u8; 32] { + BigInt::to_le_bytes(&felt.0) + } + + pub fn unsafe_from_le_bytes(buf: &[u8; 32]) -> Felt { + let big = BigInt::from_le_bytes(buf); + Felt(big) + } + + pub fn to_be_bytes(felt: &Felt) -> [u8; 32] { + BigInt::to_be_bytes(&felt.0) + } + + pub fn unsafe_from_be_bytes(buf: &[u8; 32]) -> Felt { + let big = BigInt::from_be_bytes(buf); + Felt(big) + } + // convert to Montgomery representation pub fn to_mont(fld: &Felt) -> Mont { Mont::unsafe_convert_from_big( &fld.0 ) @@ -69,6 +93,8 @@ impl Felt { Felt(Mont::convert_to_big(&mont)) } + //------------------------------------ + pub fn zero() -> Felt { Felt(BigInt::zero()) } diff --git a/src/bn254/montgomery.rs b/src/bn254/montgomery.rs index 413424c..77ee094 100644 --- a/src/bn254/montgomery.rs +++ b/src/bn254/montgomery.rs @@ -39,6 +39,34 @@ impl Mont { Mont(BigInt::make(xs)) } + pub fn is_valid(mont: &Mont) -> bool { + BigInt::is_lt_prime(&mont.0) + } + + // note: this exports the Montgomery representation! + pub fn to_le_bytes(mont: &Mont) -> [u8; 32] { + BigInt::to_le_bytes(&mont.0) + } + + // note: this assumes the input is already in Montgomery representation! + pub fn unsafe_from_le_bytes(buf: &[u8; 32]) -> Mont { + let big = BigInt::from_le_bytes(buf); + Mont(big) + } + + // note: this exports the Montgomery representation! + pub fn to_be_bytes(mont: &Mont) -> [u8; 32] { + BigInt::to_be_bytes(&mont.0) + } + + // note: this assumes the input is already in Montgomery representation! + pub fn unsafe_from_be_bytes(buf: &[u8; 32]) -> Mont { + let big = BigInt::from_be_bytes(buf); + Mont(big) + } + + //------------------------------------ + #[inline(always)] pub fn zero() -> Mont { Mont(BigInt::zero())