- 
                Notifications
    You must be signed in to change notification settings 
- Fork 135
Wasm-friendly Field #2638
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
          
     Draft
      
      
            mitschabaude
  wants to merge
  19
  commits into
  develop
  
    
      
        
          
  
    
      Choose a base branch
      
     
    
      
        
      
      
        
          
          
        
        
          
            
              
              
              
  
           
        
        
          
            
              
              
           
        
       
     
  
        
          
            
          
            
          
        
       
    
      
from
perf/wasm-friendly-field
  
      
      
   
  
    
  
  
  
 
  
      
    base: develop
Could not load branches
            
              
  
    Branch not found: {{ refName }}
  
            
                
      Loading
              
            Could not load tags
            
            
              Nothing to show
            
              
  
            
                
      Loading
              
            Are you sure you want to change the base?
            Some commits from the old base branch may be removed from the timeline,
            and old review comments may become outdated.
          
          
  
     Draft
                    Wasm-friendly Field #2638
Changes from 18 commits
      Commits
    
    
            Show all changes
          
          
            19 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      b250a52
              
                minimize assumptions on field trait for poseidon impl
              
              
                mitschabaude 498aa67
              
                move into src/pasta
              
              
                mitschabaude a16a19c
              
                reduce minimal field definition to 3 functions and 3 constants
              
              
                mitschabaude 22b42c1
              
                rename, minor changes
              
              
                mitschabaude c298437
              
                start implementing one backend
              
              
                mitschabaude a87bc0a
              
                multiplication, start trying to satisfy backend trait
              
              
                mitschabaude c5c2ada
              
                make it compile
              
              
                mitschabaude 0d17ab2
              
                export fp9 type
              
              
                mitschabaude df262a1
              
                add coercion from fp to fp9
              
              
                mitschabaude 56dd6e9
              
                add fp9 poseidon benchmark
              
              
                mitschabaude d99a80f
              
                fix conversion from arkworks fp, and hard-code field constants
              
              
                mitschabaude 6ed627c
              
                print field elements, poseidon result doesn't match yet
              
              
                mitschabaude 9a8e7d5
              
                cargo fmt
              
              
                mitschabaude 3ee9552
              
                fix some clippy warnings, ignore others
              
              
                mitschabaude a2422ff
              
                fix comments
              
              
                mitschabaude 9e33755
              
                Add benchmarks for fp9/fp conversion
              
              
                volhovm bd5d6c6
              
                Improve benchmarks for basic ops
              
              
                volhovm f73a324
              
                More benchmarks
              
              
                volhovm 6795ad9
              
                Fix for qi/carry computation for fp9
              
              
                volhovm File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
      
      Oops, something went wrong.
      
    
  
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,213 @@ | ||
| /** | ||
| * Implementation of `FpBackend` for N=9, using 29-bit limbs represented by `u32`s. | ||
| */ | ||
| use super::bigint32::BigInt; | ||
| use super::wasm_fp::{Fp, FpBackend}; | ||
|  | ||
| type B = [u32; 9]; | ||
| type B64 = [u64; 9]; | ||
|  | ||
| const SHIFT: u32 = 29; | ||
| const MASK: u32 = (1 << SHIFT) - 1; | ||
|  | ||
| const SHIFT64: u64 = SHIFT as u64; | ||
| const MASK64: u64 = MASK as u64; | ||
|  | ||
| pub const fn from_64x4(pa: [u64; 4]) -> [u32; 9] { | ||
| let mut p = [0u32; 9]; | ||
| p[0] = (pa[0] & MASK64) as u32; | ||
| p[1] = ((pa[0] >> 29) & MASK64) as u32; | ||
| p[2] = (((pa[0] >> 58) | (pa[1] << 6)) & MASK64) as u32; | ||
| p[3] = ((pa[1] >> 23) & MASK64) as u32; | ||
| p[4] = (((pa[1] >> 52) | (pa[2] << 12)) & MASK64) as u32; | ||
| p[5] = ((pa[2] >> 17) & MASK64) as u32; | ||
| p[6] = (((pa[2] >> 46) | (pa[3] << 18)) & MASK64) as u32; | ||
| p[7] = ((pa[3] >> 11) & MASK64) as u32; | ||
| p[8] = (pa[3] >> 40) as u32; | ||
| p | ||
| } | ||
| pub const fn to_64x4(pa: [u32; 9]) -> [u64; 4] { | ||
| let mut p = [0u64; 4]; | ||
| p[0] = pa[0] as u64; | ||
| p[0] |= (pa[1] as u64) << 29; | ||
| p[0] |= (pa[2] as u64) << 58; | ||
| p[1] = (pa[2] as u64) >> 6; | ||
| p[1] |= (pa[3] as u64) << 23; | ||
| p[1] |= (pa[4] as u64) << 52; | ||
| p[2] = (pa[4] as u64) >> 12; | ||
| p[2] |= (pa[5] as u64) << 17; | ||
| p[2] |= (pa[6] as u64) << 46; | ||
| p[3] = (pa[6] as u64) >> 18; | ||
| p[3] |= (pa[7] as u64) << 11; | ||
| p[3] |= (pa[8] as u64) << 40; | ||
| p | ||
| } | ||
|  | ||
| pub trait FpConstants: Send + Sync + 'static + Sized { | ||
| const MODULUS: B; | ||
| const MODULUS64: B64 = { | ||
| let mut modulus64 = [0u64; 9]; | ||
| let modulus = Self::MODULUS; | ||
| let mut i = 0; | ||
| while i < 9 { | ||
| modulus64[i] = modulus[i] as u64; | ||
| i += 1; | ||
| } | ||
| modulus64 | ||
| }; | ||
|  | ||
| /// montgomery params | ||
| /// TODO: compute these | ||
| const R: B; // R = 2^261 mod modulus | ||
| const R2: B; // R^2 mod modulus | ||
| const MINV: u64; // -modulus^(-1) mod 2^29, as a u64 | ||
| } | ||
|  | ||
| #[inline] | ||
| fn gte_modulus<FpC: FpConstants>(x: &B) -> bool { | ||
| for i in (0..9).rev() { | ||
| // don't fix warning -- that makes it 15% slower! | ||
| #[allow(clippy::comparison_chain)] | ||
| if x[i] > FpC::MODULUS[i] { | ||
| return true; | ||
| } else if x[i] < FpC::MODULUS[i] { | ||
| return false; | ||
| } | ||
| } | ||
| true | ||
| } | ||
|  | ||
| // TODO performance ideas to test: | ||
| // - unroll loops | ||
| // - introduce locals for a[i] instead of accessing memory multiple times | ||
| // - only do 1 carry pass at the end, by proving properties of greater-than on uncarried result | ||
| // - use cheaper, approximate greater-than check a[8] > Fp::MODULUS[8] | ||
| pub fn add_assign<FpC: FpConstants>(x: &mut B, y: &B) { | ||
| let mut tmp: u32; | ||
| let mut carry: i32 = 0; | ||
|  | ||
| for i in 0..9 { | ||
| tmp = x[i] + y[i] + (carry as u32); | ||
| carry = (tmp as i32) >> SHIFT; | ||
| x[i] = tmp & MASK; | ||
| } | ||
|  | ||
| if gte_modulus::<FpC>(x) { | ||
| carry = 0; | ||
| #[allow(clippy::needless_range_loop)] | ||
| for i in 0..9 { | ||
| tmp = x[i].wrapping_sub(FpC::MODULUS[i]) + (carry as u32); | ||
| carry = (tmp as i32) >> SHIFT; | ||
| x[i] = tmp & MASK; | ||
| } | ||
| } | ||
| } | ||
|  | ||
| #[inline] | ||
| fn conditional_reduce<FpC: FpConstants>(x: &mut B) { | ||
| if gte_modulus::<FpC>(x) { | ||
| #[allow(clippy::needless_range_loop)] | ||
| for i in 0..9 { | ||
| x[i] = x[i].wrapping_sub(FpC::MODULUS[i]); | ||
| } | ||
| #[allow(clippy::needless_range_loop)] | ||
| for i in 1..9 { | ||
| x[i] += ((x[i - 1] as i32) >> SHIFT) as u32; | ||
| } | ||
| #[allow(clippy::needless_range_loop)] | ||
| for i in 0..8 { | ||
| x[i] &= MASK; | ||
| } | ||
| } | ||
| } | ||
|  | ||
| /// Montgomery multiplication | ||
| pub fn mul_assign<FpC: FpConstants>(x: &mut B, y: &B) { | ||
| // load y[i] into local u64s | ||
| // TODO make sure these are locals | ||
| let mut y_local = [0u64; 9]; | ||
| for i in 0..9 { | ||
| y_local[i] = y[i] as u64; | ||
| } | ||
|  | ||
| // locals for result | ||
| let mut z = [0u64; 8]; | ||
| let mut tmp: u64; | ||
|  | ||
| // main loop, without intermediate carries except for z0 | ||
| #[allow(clippy::needless_range_loop)] | ||
| for i in 0..9 { | ||
| let xi = x[i] as u64; | ||
|  | ||
| // compute qi and carry z0 result to z1 before discarding z0 | ||
| tmp = xi * y_local[0]; | ||
| let qi = ((tmp & MASK64) * FpC::MINV) & MASK64; | ||
| z[1] += (tmp + qi * FpC::MODULUS64[0]) >> SHIFT64; | ||
|  | ||
| // compute zi and shift in one step | ||
| for j in 1..8 { | ||
| z[j - 1] = z[j] + (xi * y_local[j]) + (qi * FpC::MODULUS64[j]); | ||
| } | ||
| // for j=8 we save an addition since z[8] is never needed | ||
| z[7] = xi * y_local[8] + qi * FpC::MODULUS64[8]; | ||
| } | ||
|  | ||
| // final carry pass, store result back into x | ||
| x[0] = (z[0] & MASK64) as u32; | ||
| for i in 1..8 { | ||
| x[i] = (((z[i - 1] >> SHIFT64) + z[i]) & MASK64) as u32; | ||
| } | ||
| x[8] = (z[7] >> SHIFT64) as u32; | ||
|  | ||
| // at this point, x is guaranteed to be less than 2*MODULUS | ||
| // conditionally subtract the modulus to bring it back into the canonical range | ||
| conditional_reduce::<FpC>(x); | ||
| } | ||
|  | ||
| // implement FpBackend given FpConstants | ||
|  | ||
| pub fn from_bigint_unsafe<FpC: FpConstants>(x: BigInt<9>) -> Fp<FpC, 9> { | ||
| let mut r = x.0; | ||
| // convert to montgomery form | ||
| mul_assign::<FpC>(&mut r, &FpC::R2); | ||
| Fp(BigInt(r), Default::default()) | ||
| } | ||
|  | ||
| impl<FpC: FpConstants> FpBackend<9> for FpC { | ||
| const MODULUS: BigInt<9> = BigInt(Self::MODULUS); | ||
| const ZERO: BigInt<9> = BigInt([0; 9]); | ||
| const ONE: BigInt<9> = BigInt(Self::R); | ||
|  | ||
| fn add_assign(x: &mut Fp<Self, 9>, y: &Fp<Self, 9>) { | ||
| add_assign::<Self>(&mut x.0 .0, &y.0 .0); | ||
| } | ||
|  | ||
| fn mul_assign(x: &mut Fp<Self, 9>, y: &Fp<Self, 9>) { | ||
| mul_assign::<Self>(&mut x.0 .0, &y.0 .0); | ||
| } | ||
|  | ||
| fn from_bigint(x: BigInt<9>) -> Option<Fp<Self, 9>> { | ||
| if gte_modulus::<Self>(&x.0) { | ||
| None | ||
| } else { | ||
| Some(from_bigint_unsafe(x)) | ||
| } | ||
| } | ||
| fn to_bigint(x: Fp<Self, 9>) -> BigInt<9> { | ||
| let one = [1, 0, 0, 0, 0, 0, 0, 0, 0]; | ||
| let mut r = x.0 .0; | ||
| // convert back from montgomery form | ||
| mul_assign::<Self>(&mut r, &one); | ||
| BigInt(r) | ||
| } | ||
|  | ||
| fn pack(x: Fp<Self, 9>) -> Vec<u64> { | ||
| let x = Self::to_bigint(x).0; | ||
| let x64 = to_64x4(x); | ||
| let mut res = Vec::with_capacity(4); | ||
| for limb in x64.iter() { | ||
| res.push(*limb); | ||
| } | ||
| res | ||
| } | ||
| } | ||
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| /** | ||
| * BigInt with 32-bit limbs | ||
| * | ||
| * Contains everything for wasm_fp which is unrelated to being a field | ||
| * | ||
| * Code is mostly copied from ark-ff::BigInt | ||
| */ | ||
| use ark_serialize::{ | ||
| CanonicalDeserialize, CanonicalSerialize, Compress, Read, SerializationError, Valid, Validate, | ||
| Write, | ||
| }; | ||
|  | ||
| #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] | ||
| pub struct BigInt<const N: usize>(pub [u32; N]); | ||
|  | ||
| impl<const N: usize> Default for BigInt<N> { | ||
| fn default() -> Self { | ||
| Self([0u32; N]) | ||
| } | ||
| } | ||
|  | ||
| impl<const N: usize> CanonicalSerialize for BigInt<N> { | ||
| fn serialize_with_mode<W: Write>( | ||
| &self, | ||
| writer: W, | ||
| compress: Compress, | ||
| ) -> Result<(), SerializationError> { | ||
| self.0.serialize_with_mode(writer, compress) | ||
| } | ||
|  | ||
| fn serialized_size(&self, compress: Compress) -> usize { | ||
| self.0.serialized_size(compress) | ||
| } | ||
| } | ||
|  | ||
| impl<const N: usize> Valid for BigInt<N> { | ||
| fn check(&self) -> Result<(), SerializationError> { | ||
| self.0.check() | ||
| } | ||
| } | ||
|  | ||
| impl<const N: usize> CanonicalDeserialize for BigInt<N> { | ||
| fn deserialize_with_mode<R: Read>( | ||
| reader: R, | ||
| compress: Compress, | ||
| validate: Validate, | ||
| ) -> Result<Self, SerializationError> { | ||
| Ok(BigInt::<N>(<[u32; N]>::deserialize_with_mode( | ||
| reader, compress, validate, | ||
| )?)) | ||
| } | ||
| } | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| use ark_ff::{BitIteratorBE, One, Zero}; | ||
| use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; | ||
| use std::ops::{Add, AddAssign, Mul, MulAssign}; | ||
|  | ||
| /** | ||
| * Minimal Field trait needed to implement Poseidon | ||
| */ | ||
| pub trait MinimalField: | ||
| 'static | ||
| + Copy | ||
| + Clone | ||
| + CanonicalSerialize | ||
| + CanonicalDeserialize | ||
| + Zero | ||
| + One | ||
| + for<'a> Add<&'a Self, Output = Self> | ||
| + for<'a> Mul<&'a Self, Output = Self> | ||
| + for<'a> AddAssign<&'a Self> | ||
| + for<'a> MulAssign<&'a Self> | ||
| { | ||
| /// Squares `self` in place. | ||
| fn square_in_place(&mut self) -> &mut Self; | ||
|  | ||
| /// Returns `self^exp`, where `exp` is an integer represented with `u64` limbs, | ||
| /// least significant limb first. | ||
| fn pow<S: AsRef<[u64]>>(&self, exp: S) -> Self { | ||
| let mut res = Self::one(); | ||
|  | ||
| for i in BitIteratorBE::without_leading_zeros(exp) { | ||
| res.square_in_place(); | ||
|  | ||
| if i { | ||
| res *= self; | ||
| } | ||
| } | ||
| res | ||
| } | ||
| } | ||
|  | ||
| impl<F: ark_ff::Field> MinimalField for F { | ||
| fn square_in_place(&mut self) -> &mut Self { | ||
| self.square_in_place() | ||
| } | ||
| } | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| pub mod bigint32; | ||
| pub use bigint32::BigInt; | ||
|  | ||
| pub mod minimal_field; | ||
| pub use minimal_field::MinimalField; | ||
|  | ||
| pub mod wasm_fp; | ||
| pub use wasm_fp::Fp; | ||
|  | ||
| pub mod backend9; | ||
| pub mod pasta; | ||
| pub use pasta::Fp9; | 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| use super::{backend9, wasm_fp}; | ||
| use crate::pasta::Fp; | ||
| use ark_ff::PrimeField; | ||
|  | ||
| pub struct Fp9Parameters; | ||
|  | ||
| impl backend9::FpConstants for Fp9Parameters { | ||
| const MODULUS: [u32; 9] = [ | ||
| 0x1, 0x9698768, 0x133e46e6, 0xd31f812, 0x224, 0x0, 0x0, 0x0, 0x400000, | ||
| ]; | ||
| const R: [u32; 9] = [ | ||
| 0x1fffff81, 0x14a5d367, 0x141ad3c0, 0x1435eec5, 0x1ffeefef, 0x1fffffff, 0x1fffffff, | ||
| 0x1fffffff, 0x3fffff, | ||
| ]; | ||
| const R2: [u32; 9] = [ | ||
| 0x3b6a, 0x19c10910, 0x1a6a0188, 0x12a4fd88, 0x634b36d, 0x178792ba, 0x7797a99, 0x1dce5b8a, | ||
| 0x3506bd, | ||
| ]; | ||
| const MINV: u64 = 0x1fffffff; | ||
| } | ||
| pub type Fp9 = wasm_fp::Fp<Fp9Parameters, 9>; | ||
|  | ||
| impl Fp9 { | ||
| pub fn from_fp(fp: Fp) -> Self { | ||
| backend9::from_bigint_unsafe(super::BigInt(backend9::from_64x4(fp.into_bigint().0))) | ||
| } | ||
| } | ||
|  | ||
| impl From<Fp> for Fp9 { | ||
| fn from(fp: Fp) -> Self { | ||
| Fp9::from_fp(fp) | ||
| } | ||
| } | 
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
Uh oh!
There was an error while loading. Please reload this page.