diff --git a/README.md b/README.md index b47e13e..371822e 100644 --- a/README.md +++ b/README.md @@ -87,13 +87,13 @@ On modern 64-bit CPU-s, the 64-bit version would be preferred (TODO: implement i ### TODO -- [ ] clean up the code and make it more idiomatic -- [ ] optimize squaring to use less multiplications (?) +- [x] clean up the code and make it more idiomatic +- [x] implement `circomlib`-compatible Poseidon - [x] benchmark RISC-V cycles - [ ] add more Poseidon2 state widths (not just `t=3`) -- [x] implement `circomlib`-compatible Poseidon - [ ] add a proper test-suite; in particular, more complete testing of the field operations - [ ] add a 64 bit version -- [ ] further optimizations (?) - [ ] implement the sponge construction +- [ ] optimize squaring to use less multiplications (?) +- [ ] investigate further optimization possibilities (?) diff --git a/src/bn254/mod.rs b/src/bn254/mod.rs index 766bea9..1e0841c 100644 --- a/src/bn254/mod.rs +++ b/src/bn254/mod.rs @@ -6,3 +6,5 @@ pub mod constant; pub mod montgomery; pub mod field; +#[cfg(test)] +mod test; \ No newline at end of file diff --git a/src/bn254/platform/unstable.rs b/src/bn254/platform/unstable.rs index 9cc9a0e..ea4aa5e 100644 --- a/src/bn254/platform/unstable.rs +++ b/src/bn254/platform/unstable.rs @@ -1,25 +1,16 @@ -// "unstable" version +// "unstable" (in rust parlance) version #![allow(dead_code)] #![allow(non_snake_case)] //------------------------------------------------------------------------------ -const U32_MASK: u64 = 0x_FFFF_FFFF; - #[inline(always)] pub fn boolToU32(c: bool) -> u32 { if c { 1 } else { 0 } } -#[inline(always)] -pub fn takeApart64(x: u64) -> (u32,u32) { - let lo: u32 = (x & U32_MASK) as u32; - let hi: u32 = (x >> 32 ) as u32; - (lo,hi) -} - //------------------------------------------------------------------------------ #[inline(always)] @@ -79,60 +70,3 @@ pub fn u64AddAdd32(xy: (u32,u32), a: u32, b: u32) -> (u32,u32) { } //------------------------------------------------------------------------------ -// "portable" version - -/* - -pub fn addCarry32_(x: u32, y: u32) -> (u32,bool) { - let z: u32 = u32::wrapping_add(x, y); - let c: bool = z < x; - (z, c) -} - -pub fn subBorrow32_(x: u32, y: u32) -> (u32,bool) { - let z: u32 = u32::wrapping_sub(x, y); - let c: bool = z > x; - (z, c) -} - -pub fn addCarry32(x :u32, y: u32, cin: bool) -> (u32,bool) { - let z: u32 = u32::wrapping_add(u32::wrapping_add(x, y), boolToU32(cin)); - let c: bool = if cin { z <= x } else { z < x }; - (z, c) -} - -pub fn subBorrow32(x: u32, y: u32, cin: bool) -> (u32,bool) { - let z: u32 = u32::wrapping_sub(u32::wrapping_sub(x, y), boolToU32(cin)); - let c: bool = if cin { z >= x } else { z > x }; - (z, c) -} - -pub fn mulTrunc32(x: u32, y: u32) -> u32 { - u32::wrapping_mul(x,y) -} - -pub fn mulExt32(x: u32, y: u32) -> (u32,u32) { - let z: u64 = (x as u64) * (y as u64); - let hi = (z >> 32) as u32; - let lo = (z & U32_MASK) as u32; - (lo, hi) -} - -pub fn mulAdd32(x: u32, y: u32, a: u32) -> (u32,u32) { - let z: u64 = (x as u64) * (y as u64) + (a as u64); - let hi = (z >> 32) as u32; - let lo = (z & U32_MASK) as u32; - (lo, hi) -} - -pub fn mulAddAdd32(x: u32, y: u32, a: u32, b: u32) -> (u32,u32) { - let z: u64 = (x as u64) * (y as u64) + (a as u64) + (b as u64); - let hi = (z >> 32) as u32; - let lo = (z & U32_MASK) as u32; - (lo, hi) -} - -*/ - -//------------------------------------------------------------------------------ - diff --git a/src/bn254/test/bigint.rs b/src/bn254/test/bigint.rs new file mode 100644 index 0000000..d10ef71 --- /dev/null +++ b/src/bn254/test/bigint.rs @@ -0,0 +1,52 @@ + +// tests for bigints + +#![allow(unused)] + +use crate::bn254::bigint::*; +use crate::bn254::test::properties::*; + +type Big = BigInt<8>; + +//------------------------------------------------------------------------------ + +#[test] +fn zero_is_zero() { + assert!( Big::is_zero( Big::zero() ) ) +} + +#[test] +fn unit_to_decimal() { + let x: Big = Big::make( [ 0xff74e7f5 , 0x86ab86c2 , 0x7829f01b , 0x6dff3d9f , 0x7c6194d1 , 0x58fce839 , 0x1c3fc759 , 0x0ee7c7b9 ] ); + assert_eq!( Big::to_decimal_string(x) , "6741899990217662167434591118162422674873486558668509109681649862022285027317" ) +} + +#[test] +fn unit_to_hex() { + let x: Big = Big::make( [ 0x5d8aa877 , 0x40b543d5 , 0x115812cd , 0x0563e2bd , 0x26c9552a , 0x1890edd6 , 0x803b772b , 0x1a12005c ] ); + assert_eq!( Big::to_hex_string(x) , "0x1a12005c803b772b1890edd626c9552a0563e2bd115812cd40b543d55d8aa877" ) +} + +//------------------------------------------------------------------------------ + +fn prop_from_to_bytes_le(x: BigInt) -> bool where [(); 4*N]: { + let bs: [u8; 4*N] = BigInt::to_le_bytes(x); + BigInt::::from_le_bytes(bs) == x +} + +fn prop_to_from_bytes_le(bs: [u8; 4*N]) -> bool { + let y: BigInt = BigInt::from_le_bytes(bs); + BigInt::::to_le_bytes(y) == bs +} + +fn prop_from_to_bytes_be(x: BigInt) -> bool where [(); 4*N]: { + let bs: [u8; 4*N] = BigInt::to_be_bytes(x); + BigInt::::from_be_bytes(bs) == x +} + +fn prop_to_from_bytes_be(bs: [u8; 4*N]) -> bool { + let y: BigInt = BigInt::from_be_bytes(bs); + BigInt::::to_be_bytes(y) == bs +} + +//------------------------------------------------------------------------------ diff --git a/src/bn254/test/mod.rs b/src/bn254/test/mod.rs new file mode 100644 index 0000000..2734ee1 --- /dev/null +++ b/src/bn254/test/mod.rs @@ -0,0 +1,6 @@ + +pub mod properties; + +pub mod bigint; +// pub mod field; +// pub mod mont; \ No newline at end of file diff --git a/src/bn254/test/properties.rs b/src/bn254/test/properties.rs new file mode 100644 index 0000000..f6c067d --- /dev/null +++ b/src/bn254/test/properties.rs @@ -0,0 +1,113 @@ + +// field properties + +#![allow(dead_code)] +#![allow(non_snake_case)] + +use std::cmp::{Eq}; +use std::ops::{Neg,Add,Sub,Mul,Div}; + +//------------------------------------------------------------------------------ + +pub trait Group = Copy + Clone + Default + From + Eq + Neg + Add + Sub; +pub trait Ring = Group + Mul; +pub trait Field = Ring + Div; + +//------------------------------------------------------------------------------ + +fn zero() -> A { + A::default() +} + +fn small(x: u32) -> A { + A::from(x) +} + +// wtf rust? +fn xneg>(x: A ) -> A { + -x +} + +//------------------------------------------------------------------------------ + +pub fn prop_left_additive_unit(x: A) -> bool { + zero::() + x == x +} + +pub fn prop_right_additive_unit(x: A) -> bool { + x + zero::() == x +} + +pub fn prop_sub_zero(x: A) -> bool { + x - zero::() == x +} + +pub fn prop_zero_subo(x: A) -> bool { + zero::() - x == xneg(x) +} + +pub fn prop_add_commutative(x: A, y: A) -> bool { + x + y == y + x +} + +pub fn prop_sub_anticommutative(x: A, y: A) -> bool { + x - y == xneg( y - x ) +} + +pub fn prop_neg_involutive(x: A) -> bool { + xneg( xneg(x) ) == x +} + +pub fn prop_add_sub(x: A, y: A) -> bool { + (x + y) - y == x +} + +pub fn prop_sub_add(x: A, y: A) -> bool { + (x - y) + y == x +} + +pub fn prop_sub_neg_add(x: A, y: A) -> bool { + x - xneg(y) == x + y +} + +pub fn prop_sub_add_neg(x: A, y: A) -> bool { + x - y == x + xneg(y) +} + +//------------------------------------------------------------------------------ + +pub fn prop_twice(x: A) -> bool { + x + x == x * small::(2) +} + +pub fn prop_thrice(x: A) -> bool { + x + x + x == x * small::(3) +} + +//------------------------------------------------------------------------------ + +pub fn prop_left_multiplicative_unit(x: A) -> bool { + small::(1) * x == x +} + +pub fn prop_right_multiplicative_unit(x: A) -> bool { + x * small::(1) == x +} + +pub fn prop_mul_commutative(x: A, y: A) -> bool { + x * y == y * x +} + +pub fn prop_mul_neg(x: A, y: A) -> bool { + xneg(x * y) == xneg(x) * y +} + +pub fn prop_distributive_add(x: A, y: A, z: A) -> bool { + (x + y) * z == x * z + y * z +} + +pub fn prop_distributive_sub(x: A, y: A, z: A) -> bool { + (x - y) * z == x * z - y * z +} + +//------------------------------------------------------------------------------ diff --git a/src/poseidon/permutation.rs b/src/poseidon/permutation.rs index 8d0ee9d..574148e 100644 --- a/src/poseidon/permutation.rs +++ b/src/poseidon/permutation.rs @@ -167,4 +167,47 @@ pub fn hash4(a: Felt, b: Felt, c: Felt, d: Felt) -> Felt { compress::<4>([ a, b, c, d ]) } +//============================================================================== +// *** TESTS + +#[cfg(test)] +mod test { + + use crate::bn254::field::*; + use super::*; + + #[test] + fn hash1_kat() { + assert_eq!( + Felt::to_decimal_string( hash1( 1u32.into() ) ), + "18586133768512220936620570745912940619677854269274689475585506675881198879027" + ); + } + + #[test] + fn hash2_kat() { + assert_eq!( + Felt::to_decimal_string( hash2( 1u32.into(), 2u32.into() ) ), + "7853200120776062878684798364095072458815029376092732009249414926327459813530" + ); + } + + #[test] + fn hash3_kat() { + assert_eq!( + Felt::to_decimal_string( hash3( 1u32.into(), 2u32.into(), 3u32.into() ) ), + "6542985608222806190361240322586112750744169038454362455181422643027100751666" + ); + } + + #[test] + fn hash4_kat() { + assert_eq!( + Felt::to_decimal_string( hash4( 1u32.into(), 2u32.into(), 3u32.into(), 4u32.into() ) ), + "18821383157269793795438455681495246036402687001665670618754263018637548127333" + ); + } + +} + //------------------------------------------------------------------------------ diff --git a/src/poseidon2/permutation.rs b/src/poseidon2/permutation.rs index e8d5e5f..690ab77 100644 --- a/src/poseidon2/permutation.rs +++ b/src/poseidon2/permutation.rs @@ -83,4 +83,27 @@ pub fn permute_iterated(input: [Felt; 3], count: usize) -> [Felt; 3] { out } +//============================================================================== +// *** TESTS + +#[cfg(test)] +mod test { + + use crate::bn254::field::{Felt}; + use super::*; + + #[test] + fn permute3_kat() { + let out: [Felt; 3] = permute( [ 0u32.into() , 1u32.into() , 2u32.into() ] ); + println!(" 0 -> {}" , out[0] ); + println!(" 1 -> {}" , out[1] ); + println!(" 2 -> {}" , out[2] ); + assert_eq!( Felt::to_hex_string( out[0] ) , "0x30610a447b7dec194697fb50786aa7421494bd64c221ba4d3b1af25fb07bd103" ); + assert_eq!( Felt::to_hex_string( out[1] ) , "0x13f731d6ffbad391be22d2ac364151849e19fa38eced4e761bcd21dbdc600288" ); + assert_eq!( Felt::to_hex_string( out[2] ) , "0x1433e2c8f68382c447c5c14b8b3df7cbfd9273dd655fe52f1357c27150da786f" ); + } + +} + //------------------------------------------------------------------------------ +