├── .github ├── CODEOWNERS ├── dependabot.yml ├── workflows │ ├── linkify_changelog.yml │ └── ci.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ├── src ├── poly │ ├── evaluations │ │ ├── mod.rs │ │ └── univariate │ │ │ └── lagrange_interpolator.rs │ ├── polynomial │ │ ├── mod.rs │ │ └── univariate │ │ │ ├── mod.rs │ │ │ └── dense.rs │ ├── mod.rs │ └── domain │ │ ├── vanishing_poly.rs │ │ └── mod.rs ├── test_utils.rs ├── groups │ ├── curves │ │ └── mod.rs │ └── mod.rs ├── fields │ ├── fp2.rs │ ├── fp3.rs │ ├── fp4.rs │ ├── fp6_2over3.rs │ ├── emulated_fp │ │ ├── mul_result.rs │ │ ├── params.rs │ │ └── mod.rs │ ├── fp6_3over2.rs │ └── fp12.rs ├── boolean │ ├── convert.rs │ ├── test_utils.rs │ ├── not.rs │ ├── cmp.rs │ ├── xor.rs │ ├── select.rs │ └── or.rs ├── cmp.rs ├── uint │ ├── add │ │ ├── mod.rs │ │ ├── wrapping.rs │ │ └── saturating.rs │ ├── select.rs │ ├── not.rs │ ├── prim_uint.rs │ ├── mod.rs │ ├── shl.rs │ ├── shr.rs │ ├── eq.rs │ ├── test_utils.rs │ ├── cmp.rs │ └── rotate.rs ├── r1cs_var.rs ├── pairing │ ├── mod.rs │ └── bls12 │ │ └── mod.rs ├── lib.rs ├── select.rs ├── alloc.rs └── convert.rs ├── .gitignore ├── rustfmt.toml ├── scripts ├── install-hook.sh └── linkify_changelog.py ├── tests ├── from_test.rs └── to_constraint_field_test.rs ├── LICENSE-MIT ├── .hooks └── pre-commit ├── Cargo.toml ├── README.md ├── CONTRIBUTING.md ├── CHANGELOG.md └── benches └── bench.rs /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @arkworks-rs/maintainers 2 | -------------------------------------------------------------------------------- /src/poly/evaluations/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod univariate; 2 | -------------------------------------------------------------------------------- /src/poly/polynomial/mod.rs: -------------------------------------------------------------------------------- 1 | /// Module defining data structures for univariate polynomials. 2 | pub mod univariate; 3 | -------------------------------------------------------------------------------- /src/poly/polynomial/univariate/mod.rs: -------------------------------------------------------------------------------- 1 | /// A dense univariate polynomial represented in coefficient form. 2 | pub mod dense; 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .DS_Store 4 | .idea 5 | *.iml 6 | *.ipynb_checkpoints 7 | *.pyc 8 | *.sage.py 9 | params 10 | *.swp 11 | *.swo 12 | -------------------------------------------------------------------------------- /src/poly/mod.rs: -------------------------------------------------------------------------------- 1 | /// Evaluation domains for polynomials. 2 | pub mod domain; 3 | /// Evaluations of polynomials over domains. 4 | pub mod evaluations; 5 | /// Modules for working with polynomials in coefficient forms. 6 | pub mod polynomial; 7 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | wrap_comments = true 3 | normalize_comments = true 4 | use_try_shorthand = true 5 | match_block_trailing_comma = true 6 | use_field_init_shorthand = true 7 | edition = "2018" 8 | condense_wildcard_suffixes = true 9 | merge_imports = true 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: rand_xorshift 10 | versions: 11 | - 0.3.0 12 | - dependency-name: rand 13 | versions: 14 | - 0.8.0 15 | -------------------------------------------------------------------------------- /scripts/install-hook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/env bash 2 | # This script will install the provided directory ../.hooks as the hook 3 | # directory for the present repo. See there for hooks, including a pre-commit 4 | # hook that runs rustfmt on files before a commit. 5 | 6 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 7 | HOOKS_DIR="${DIR}/../.hooks" 8 | 9 | git config core.hooksPath "$HOOKS_DIR" 10 | -------------------------------------------------------------------------------- /src/test_utils.rs: -------------------------------------------------------------------------------- 1 | use core::iter; 2 | 3 | use crate::alloc::AllocationMode; 4 | 5 | pub(crate) fn modes() -> impl Iterator { 6 | use AllocationMode::*; 7 | [Constant, Input, Witness].into_iter() 8 | } 9 | 10 | pub(crate) fn combination( 11 | mut i: impl Iterator, 12 | ) -> impl Iterator { 13 | iter::from_fn(move || i.next().map(|t| modes().map(move |mode| (mode, t.clone())))) 14 | .flat_map(|x| x) 15 | } 16 | -------------------------------------------------------------------------------- /src/groups/curves/mod.rs: -------------------------------------------------------------------------------- 1 | /// This module generically implements arithmetic for Short 2 | /// Weierstrass elliptic curves by following the complete formulae of 3 | /// [[Renes, Costello, Batina 2015]](https://eprint.iacr.org/2015/1060). 4 | pub mod short_weierstrass; 5 | 6 | /// This module generically implements arithmetic for Twisted 7 | /// Edwards elliptic curves by following the complete formulae described in the 8 | /// [EFD](https://www.hyperelliptic.org/EFD/g1p/auto-twisted.html). 9 | pub mod twisted_edwards; 10 | -------------------------------------------------------------------------------- /.github/workflows/linkify_changelog.yml: -------------------------------------------------------------------------------- 1 | name: Linkify Changelog 2 | 3 | on: 4 | workflow_dispatch 5 | 6 | jobs: 7 | linkify: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v2 12 | - name: Add links 13 | run: python3 scripts/linkify_changelog.py CHANGELOG.md 14 | - name: Commit 15 | run: | 16 | git config user.name github-actions 17 | git config user.email github-actions@github.com 18 | git add . 19 | git commit -m "Linkify Changelog" 20 | git push 21 | -------------------------------------------------------------------------------- /src/fields/fp2.rs: -------------------------------------------------------------------------------- 1 | use crate::fields::{fp::FpVar, quadratic_extension::*}; 2 | use ark_ff::fields::{Fp2Config, Fp2ConfigWrapper, QuadExtConfig}; 3 | 4 | /// A quadratic extension field constructed over a prime field. 5 | /// This is the R1CS equivalent of `ark_ff::Fp2

`. 6 | pub type Fp2Var

= QuadExtVar::Fp>, Fp2ConfigWrapper

>; 7 | 8 | impl QuadExtVarConfig> for Fp2ConfigWrapper

{ 9 | fn mul_base_field_var_by_frob_coeff(fe: &mut FpVar, power: usize) { 10 | *fe *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/fields/fp3.rs: -------------------------------------------------------------------------------- 1 | use crate::fields::{cubic_extension::*, fp::FpVar}; 2 | use ark_ff::{ 3 | fields::{CubicExtConfig, Fp3ConfigWrapper}, 4 | Fp3Config, 5 | }; 6 | 7 | /// A cubic extension field constructed over a prime field. 8 | /// This is the R1CS equivalent of `ark_ff::Fp3

`. 9 | pub type Fp3Var

= CubicExtVar::Fp>, Fp3ConfigWrapper

>; 10 | 11 | impl CubicExtVarConfig> for Fp3ConfigWrapper

{ 12 | fn mul_base_field_vars_by_frob_coeff( 13 | c1: &mut FpVar, 14 | c2: &mut FpVar, 15 | power: usize, 16 | ) { 17 | *c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; 18 | *c2 *= Self::FROBENIUS_COEFF_C2[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/fields/fp4.rs: -------------------------------------------------------------------------------- 1 | use crate::fields::{fp2::Fp2Var, quadratic_extension::*}; 2 | use ark_ff::{ 3 | fields::{Fp4ConfigWrapper, QuadExtConfig}, 4 | Fp4Config, 5 | }; 6 | 7 | /// A quartic extension field constructed as the tower of a 8 | /// quadratic extension over a quadratic extension field. 9 | /// This is the R1CS equivalent of `ark_ff::Fp4

`. 10 | pub type Fp4Var

= QuadExtVar::Fp2Config>, Fp4ConfigWrapper

>; 11 | 12 | impl QuadExtVarConfig> for Fp4ConfigWrapper

{ 13 | fn mul_base_field_var_by_frob_coeff(fe: &mut Fp2Var, power: usize) { 14 | fe.c0 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; 15 | fe.c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/boolean/convert.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::convert::{ToBytesGadget, ToConstraintFieldGadget}; 3 | 4 | impl ToBytesGadget for Boolean { 5 | /// Outputs `1u8` if `self` is true, and `0u8` otherwise. 6 | #[tracing::instrument(target = "r1cs")] 7 | fn to_bytes_le(&self) -> Result>, SynthesisError> { 8 | let value = self.value().map(u8::from).ok(); 9 | let mut bits = [Boolean::FALSE; 8]; 10 | bits[0] = self.clone(); 11 | Ok(vec![UInt8 { bits, value }]) 12 | } 13 | } 14 | 15 | impl ToConstraintFieldGadget for Boolean { 16 | #[tracing::instrument(target = "r1cs")] 17 | fn to_constraint_field(&self) -> Result>, SynthesisError> { 18 | let var = From::from(self.clone()); 19 | Ok(vec![var]) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us squash bugs! 4 | 5 | --- 6 | 7 | ∂ 12 | 13 | ## Summary of Bug 14 | 15 | 16 | 17 | ## Version 18 | 19 | 20 | 21 | ## Steps to Reproduce 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/fields/fp6_2over3.rs: -------------------------------------------------------------------------------- 1 | use crate::fields::{fp3::Fp3Var, quadratic_extension::*}; 2 | use ark_ff::{fields::fp6_2over3::*, QuadExtConfig}; 3 | 4 | /// A sextic extension field constructed as the tower of a 5 | /// quadratic extension over a cubic extension field. 6 | /// This is the R1CS equivalent of `ark_ff::fp6_2over3::Fp6

`. 7 | pub type Fp6Var

= QuadExtVar::Fp3Config>, Fp6ConfigWrapper

>; 8 | 9 | impl QuadExtVarConfig> for Fp6ConfigWrapper

{ 10 | fn mul_base_field_var_by_frob_coeff(fe: &mut Fp3Var, power: usize) { 11 | fe.c0 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; 12 | fe.c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; 13 | fe.c2 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/from_test.rs: -------------------------------------------------------------------------------- 1 | use ark_r1cs_std::{ 2 | alloc::AllocVar, 3 | fields::emulated_fp::{EmulatedFpVar, MulResultVar}, 4 | R1CSVar, 5 | }; 6 | use ark_relations::r1cs::ConstraintSystem; 7 | use ark_std::UniformRand; 8 | 9 | #[test] 10 | fn from_test() { 11 | type F = ark_bls12_377::Fr; 12 | type CF = ark_bls12_377::Fq; 13 | 14 | let mut rng = ark_std::test_rng(); 15 | let cs = ConstraintSystem::::new_ref(); 16 | let f = F::rand(&mut rng); 17 | 18 | let f_var = EmulatedFpVar::::new_input(cs.clone(), || Ok(f)).unwrap(); 19 | let f_var_converted = MulResultVar::::from(&f_var); 20 | let f_var_converted_reduced = f_var_converted.reduce().unwrap(); 21 | 22 | let f_var_value = f_var.value().unwrap(); 23 | let f_var_converted_reduced_value = f_var_converted_reduced.value().unwrap(); 24 | 25 | assert_eq!(f_var_value, f_var_converted_reduced_value); 26 | } 27 | -------------------------------------------------------------------------------- /tests/to_constraint_field_test.rs: -------------------------------------------------------------------------------- 1 | use ark_r1cs_std::{ 2 | alloc::AllocVar, convert::ToConstraintFieldGadget, fields::emulated_fp::EmulatedFpVar, R1CSVar, 3 | }; 4 | use ark_relations::r1cs::ConstraintSystem; 5 | 6 | #[test] 7 | fn to_constraint_field_test() { 8 | type F = ark_bls12_377::Fr; 9 | type CF = ark_bls12_377::Fq; 10 | 11 | let cs = ConstraintSystem::::new_ref(); 12 | 13 | let a = EmulatedFpVar::Constant(F::from(12u8)); 14 | let b = EmulatedFpVar::new_input(cs.clone(), || Ok(F::from(6u8))).unwrap(); 15 | 16 | let b2 = &b + &b; 17 | 18 | let a_to_constraint_field = a.to_constraint_field().unwrap(); 19 | let b2_to_constraint_field = b2.to_constraint_field().unwrap(); 20 | 21 | assert_eq!(a_to_constraint_field.len(), b2_to_constraint_field.len()); 22 | for (left, right) in a_to_constraint_field 23 | .iter() 24 | .zip(b2_to_constraint_field.iter()) 25 | { 26 | assert_eq!(left.value(), right.value()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Create a proposal to request a feature 4 | 5 | --- 6 | 7 | 13 | 14 | ## Summary 15 | 16 | 17 | 18 | ## Problem Definition 19 | 20 | 23 | 24 | ## Proposal 25 | 26 | 27 | 28 | ____ 29 | 30 | #### For Admin Use 31 | 32 | - [ ] Not duplicate issue 33 | - [ ] Appropriate labels applied 34 | - [ ] Appropriate contributors tagged 35 | - [ ] Contributor assigned/self-assigned 36 | -------------------------------------------------------------------------------- /.hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rustfmt --version &>/dev/null 4 | if [ $? != 0 ]; then 5 | printf "[pre_commit] \033[0;31merror\033[0m: \"rustfmt\" not available. \n" 6 | printf "[pre_commit] \033[0;31merror\033[0m: rustfmt can be installed via - \n" 7 | printf "[pre_commit] $ rustup component add rustfmt \n" 8 | exit 1 9 | fi 10 | 11 | problem_files=() 12 | 13 | # collect ill-formatted files 14 | for file in $(git diff --name-only --cached); do 15 | if [ ${file: -3} == ".rs" ]; then 16 | rustfmt +stable --check $file &>/dev/null 17 | if [ $? != 0 ]; then 18 | problem_files+=($file) 19 | fi 20 | fi 21 | done 22 | 23 | if [ ${#problem_files[@]} == 0 ]; then 24 | # done 25 | printf "[pre_commit] rustfmt \033[0;32mok\033[0m \n" 26 | else 27 | # reformat the files that need it and re-stage them. 28 | printf "[pre_commit] the following files were rustfmt'd before commit: \n" 29 | for file in ${problem_files[@]}; do 30 | rustfmt +stable $file 31 | git add $file 32 | printf "\033[0;32m $file\033[0m \n" 33 | done 34 | fi 35 | 36 | exit 0 37 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ## Description 8 | 9 | 12 | 13 | closes: #XXXX 14 | 15 | --- 16 | 17 | Before we can merge this PR, please make sure that all the following items have been 18 | checked off. If any of the checklist items are not applicable, please leave them but 19 | write a little note why. 20 | 21 | - [ ] Targeted PR against correct branch (master) 22 | - [ ] Linked to Github issue with discussion and accepted design OR have an explanation in the PR that describes this work. 23 | - [ ] Wrote unit tests 24 | - [ ] Updated relevant documentation in the code 25 | - [ ] Added a relevant changelog entry to the `Pending` section in `CHANGELOG.md` 26 | - [ ] Re-reviewed `Files changed` in the Github PR explorer 27 | -------------------------------------------------------------------------------- /scripts/linkify_changelog.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sys 3 | import fileinput 4 | import os 5 | 6 | # Set this to the name of the repo, if you don't want it to be read from the filesystem. 7 | # It assumes the changelog file is in the root of the repo. 8 | repo_name = "" 9 | 10 | # This script goes through the provided file, and replaces any " \#", 11 | # with the valid mark down formatted link to it. e.g. 12 | # " [\#number](https://github.com/arkworks-rs/template/pull/) 13 | # Note that if the number is for a an issue, github will auto-redirect you when you click the link. 14 | # It is safe to run the script multiple times in succession. 15 | # 16 | # Example usage $ python3 linkify_changelog.py ../CHANGELOG.md 17 | if len(sys.argv) < 2: 18 | print("Must include path to changelog as the first argument to the script") 19 | print("Example Usage: python3 linkify_changelog.py ../CHANGELOG.md") 20 | exit() 21 | 22 | changelog_path = sys.argv[1] 23 | if repo_name == "": 24 | path = os.path.abspath(changelog_path) 25 | components = path.split(os.path.sep) 26 | repo_name = components[-2] 27 | 28 | for line in fileinput.input(inplace=True): 29 | line = re.sub(r"\- #([0-9]*)", r"- [\\#\1](https://github.com/arkworks-rs/" + repo_name + r"/pull/\1)", line.rstrip()) 30 | # edits the current file 31 | print(line) -------------------------------------------------------------------------------- /src/boolean/test_utils.rs: -------------------------------------------------------------------------------- 1 | use crate::test_utils; 2 | 3 | use super::*; 4 | use ark_relations::r1cs::{ConstraintSystem, SynthesisError}; 5 | 6 | pub(crate) fn test_unary_op( 7 | a: bool, 8 | mode: AllocationMode, 9 | test: impl FnOnce(Boolean) -> Result<(), SynthesisError>, 10 | ) -> Result<(), SynthesisError> { 11 | let cs = ConstraintSystem::::new_ref(); 12 | let a = Boolean::::new_variable(cs.clone(), || Ok(a), mode)?; 13 | test(a) 14 | } 15 | 16 | pub(crate) fn test_binary_op( 17 | a: bool, 18 | b: bool, 19 | mode_a: AllocationMode, 20 | mode_b: AllocationMode, 21 | test: impl FnOnce(Boolean, Boolean) -> Result<(), SynthesisError>, 22 | ) -> Result<(), SynthesisError> { 23 | let cs = ConstraintSystem::::new_ref(); 24 | let a = Boolean::::new_variable(cs.clone(), || Ok(a), mode_a)?; 25 | let b = Boolean::::new_variable(cs.clone(), || Ok(b), mode_b)?; 26 | test(a, b) 27 | } 28 | 29 | pub(crate) fn run_binary_exhaustive( 30 | test: impl Fn(Boolean, Boolean) -> Result<(), SynthesisError> + Copy, 31 | ) -> Result<(), SynthesisError> { 32 | for (mode_a, a) in test_utils::combination([false, true].into_iter()) { 33 | for (mode_b, b) in test_utils::combination([false, true].into_iter()) { 34 | test_binary_op(a, b, mode_a, mode_b, test)?; 35 | } 36 | } 37 | Ok(()) 38 | } 39 | 40 | pub(crate) fn run_unary_exhaustive( 41 | test: impl Fn(Boolean) -> Result<(), SynthesisError> + Copy, 42 | ) -> Result<(), SynthesisError> { 43 | for (mode, a) in test_utils::combination([false, true].into_iter()) { 44 | test_unary_op(a, mode, test)?; 45 | } 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /src/cmp.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::{Field, PrimeField}; 2 | use ark_relations::r1cs::SynthesisError; 3 | 4 | use crate::{boolean::Boolean, eq::EqGadget, R1CSVar}; 5 | 6 | /// Specifies how to generate constraints for comparing two variables. 7 | pub trait CmpGadget: R1CSVar + EqGadget { 8 | fn is_gt(&self, other: &Self) -> Result, SynthesisError> { 9 | other.is_lt(self) 10 | } 11 | 12 | fn is_ge(&self, other: &Self) -> Result, SynthesisError>; 13 | 14 | fn is_lt(&self, other: &Self) -> Result, SynthesisError> { 15 | Ok(!self.is_ge(other)?) 16 | } 17 | 18 | fn is_le(&self, other: &Self) -> Result, SynthesisError> { 19 | other.is_ge(self) 20 | } 21 | } 22 | 23 | /// Mimics the behavior of `std::cmp::PartialOrd` for `()`. 24 | impl CmpGadget for () { 25 | fn is_gt(&self, _other: &Self) -> Result, SynthesisError> { 26 | Ok(Boolean::FALSE) 27 | } 28 | 29 | fn is_ge(&self, _other: &Self) -> Result, SynthesisError> { 30 | Ok(Boolean::TRUE) 31 | } 32 | 33 | fn is_lt(&self, _other: &Self) -> Result, SynthesisError> { 34 | Ok(Boolean::FALSE) 35 | } 36 | 37 | fn is_le(&self, _other: &Self) -> Result, SynthesisError> { 38 | Ok(Boolean::TRUE) 39 | } 40 | } 41 | 42 | /// Mimics the lexicographic comparison behavior of `std::cmp::PartialOrd` for `[T]`. 43 | impl, F: PrimeField> CmpGadget for [T] { 44 | fn is_ge(&self, other: &Self) -> Result, SynthesisError> { 45 | let mut result = Boolean::TRUE; 46 | let mut all_equal_so_far = Boolean::TRUE; 47 | for (a, b) in self.iter().zip(other) { 48 | all_equal_so_far &= a.is_eq(b)?; 49 | result &= a.is_gt(b)? | &all_equal_so_far; 50 | } 51 | Ok(result) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/uint/add/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::fields::fp::FpVar; 2 | 3 | use super::*; 4 | 5 | mod saturating; 6 | mod wrapping; 7 | 8 | impl UInt { 9 | /// Adds up `operands`, returning the bit decomposition of the result, along with 10 | /// the value of the result. If all the operands are constant, then the bit decomposition 11 | /// is empty, and the value is the constant value of the result. 12 | /// 13 | /// # Panics 14 | /// 15 | /// This method panics if the result of addition could possibly exceed the field size. 16 | #[tracing::instrument(target = "r1cs", skip(operands, adder))] 17 | fn add_many_helper( 18 | operands: &[Self], 19 | adder: impl Fn(T, T) -> T, 20 | ) -> Result<(Vec>, Option), SynthesisError> { 21 | // Bounds on `N` to avoid overflows 22 | 23 | assert!(operands.len() >= 1); 24 | let max_value_size = N as u32 + ark_std::log2(operands.len()); 25 | assert!(max_value_size <= F::MODULUS_BIT_SIZE); 26 | 27 | if operands.len() == 1 { 28 | return Ok((operands[0].bits.to_vec(), operands[0].value)); 29 | } 30 | 31 | // Compute the value of the result. 32 | let mut value = Some(T::zero()); 33 | for op in operands { 34 | value = value.and_then(|v| Some(adder(v, op.value?))); 35 | } 36 | if operands.is_constant() { 37 | // If all operands are constant, then the result is also constant. 38 | // In this case, we can return early. 39 | return Ok((Vec::new(), value)); 40 | } 41 | 42 | // Compute the full (non-wrapped) sum of the operands. 43 | let result = operands 44 | .iter() 45 | .map(|op| Boolean::le_bits_to_fp(&op.bits).unwrap()) 46 | .sum::>(); 47 | let (result, _) = result.to_bits_le_with_top_bits_zero(max_value_size as usize)?; 48 | Ok((result, value)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/r1cs_var.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::Field; 2 | use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError}; 3 | use ark_std::vec::Vec; 4 | 5 | /// This trait describes some core functionality that is common to high-level 6 | /// variables, such as `Boolean`s, `FieldVar`s, `GroupVar`s, etc. 7 | pub trait R1CSVar { 8 | /// The type of the "native" value that `Self` represents in the constraint 9 | /// system. 10 | type Value: core::fmt::Debug + Eq + Clone; 11 | 12 | /// Returns the underlying `ConstraintSystemRef`. 13 | /// 14 | /// If `self` is a constant value, then this *must* return 15 | /// `ark_relations::r1cs::ConstraintSystemRef::None`. 16 | fn cs(&self) -> ConstraintSystemRef; 17 | 18 | /// Returns `true` if `self` is a circuit-generation-time constant. 19 | fn is_constant(&self) -> bool { 20 | self.cs().is_none() 21 | } 22 | 23 | /// Returns the value that is assigned to `self` in the underlying 24 | /// `ConstraintSystem`. 25 | fn value(&self) -> Result; 26 | } 27 | 28 | impl> R1CSVar for [T] { 29 | type Value = Vec; 30 | 31 | fn cs(&self) -> ConstraintSystemRef { 32 | let mut result = ConstraintSystemRef::None; 33 | for var in self { 34 | result = var.cs().or(result); 35 | } 36 | result 37 | } 38 | 39 | fn value(&self) -> Result { 40 | let mut result = Vec::new(); 41 | for var in self { 42 | result.push(var.value()?); 43 | } 44 | Ok(result) 45 | } 46 | } 47 | 48 | impl<'a, F: Field, T: 'a + R1CSVar> R1CSVar for &'a T { 49 | type Value = T::Value; 50 | 51 | fn cs(&self) -> ConstraintSystemRef { 52 | (*self).cs() 53 | } 54 | 55 | fn value(&self) -> Result { 56 | (*self).value() 57 | } 58 | } 59 | 60 | impl, const N: usize> R1CSVar for [T; N] { 61 | type Value = [T::Value; N]; 62 | 63 | fn cs(&self) -> ConstraintSystemRef { 64 | let mut result = ConstraintSystemRef::None; 65 | for var in self { 66 | result = var.cs().or(result); 67 | } 68 | result 69 | } 70 | 71 | fn value(&self) -> Result { 72 | Ok(core::array::from_fn(|i| self[i].value().unwrap())) 73 | } 74 | } 75 | 76 | impl R1CSVar for () { 77 | type Value = (); 78 | 79 | fn cs(&self) -> ConstraintSystemRef { 80 | ConstraintSystemRef::None 81 | } 82 | 83 | fn value(&self) -> Result { 84 | Ok(()) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/fields/emulated_fp/mul_result.rs: -------------------------------------------------------------------------------- 1 | use super::{AllocatedMulResultVar, EmulatedFpVar}; 2 | use ark_ff::PrimeField; 3 | use ark_relations::r1cs::Result as R1CSResult; 4 | 5 | /// An intermediate representation especially for the result of a 6 | /// multiplication, containing more limbs. It is intended for advanced usage to 7 | /// improve the efficiency. 8 | /// 9 | /// That is, instead of calling `mul`, one can call `mul_without_reduce` to 10 | /// obtain this intermediate representation, which can still be added. 11 | /// Then, one can call `reduce` to reduce it back to `EmulatedFpVar`. 12 | /// This may help cut the number of reduce operations. 13 | #[derive(Debug)] 14 | #[must_use] 15 | pub enum MulResultVar { 16 | /// as a constant 17 | Constant(TargetF), 18 | /// as an allocated gadget 19 | Var(AllocatedMulResultVar), 20 | } 21 | 22 | impl MulResultVar { 23 | /// Create a zero `MulResultVar` (used for additions) 24 | pub fn zero() -> Self { 25 | Self::Constant(TargetF::zero()) 26 | } 27 | 28 | /// Create an `MulResultVar` from a constant 29 | pub fn constant(v: TargetF) -> Self { 30 | Self::Constant(v) 31 | } 32 | 33 | /// Reduce the `MulResultVar` back to EmulatedFpVar 34 | #[tracing::instrument(target = "r1cs")] 35 | pub fn reduce(&self) -> R1CSResult> { 36 | match self { 37 | Self::Constant(c) => Ok(EmulatedFpVar::Constant(*c)), 38 | Self::Var(v) => Ok(EmulatedFpVar::Var(v.reduce()?)), 39 | } 40 | } 41 | } 42 | 43 | impl From<&EmulatedFpVar> 44 | for MulResultVar 45 | { 46 | fn from(src: &EmulatedFpVar) -> Self { 47 | match src { 48 | EmulatedFpVar::Constant(c) => MulResultVar::Constant(*c), 49 | EmulatedFpVar::Var(v) => { 50 | MulResultVar::Var(AllocatedMulResultVar::::from(v)) 51 | }, 52 | } 53 | } 54 | } 55 | 56 | impl_bounded_ops!( 57 | MulResultVar, 58 | TargetF, 59 | Add, 60 | add, 61 | AddAssign, 62 | add_assign, 63 | |this: &'a MulResultVar, other: &'a MulResultVar| { 64 | use MulResultVar::*; 65 | match (this, other) { 66 | (Constant(c1), Constant(c2)) => Constant(*c1 + c2), 67 | (Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.add_constant(c).unwrap()), 68 | (Var(v1), Var(v2)) => Var(v1.add(v2).unwrap()), 69 | } 70 | }, 71 | |this: &'a MulResultVar, other: TargetF| { this + &MulResultVar::Constant(other) }, 72 | (TargetF: PrimeField, BaseF: PrimeField), 73 | ); 74 | -------------------------------------------------------------------------------- /src/poly/domain/vanishing_poly.rs: -------------------------------------------------------------------------------- 1 | use crate::fields::{fp::FpVar, FieldVar}; 2 | use ark_ff::{Field, PrimeField}; 3 | use ark_relations::r1cs::SynthesisError; 4 | use ark_std::ops::Sub; 5 | 6 | /// Struct describing vanishing polynomial for a multiplicative coset H where 7 | /// |H| is a power of 2. As H is a coset, every element can be described as 8 | /// h*g^i and therefore has vanishing polynomial Z_H(x) = x^|H| - h^|H| 9 | #[derive(Clone)] 10 | pub struct VanishingPolynomial { 11 | /// h^|H| 12 | pub constant_term: F, 13 | /// log_2(|H|) 14 | pub dim_h: u64, 15 | /// |H| 16 | pub order_h: u64, 17 | } 18 | 19 | impl VanishingPolynomial { 20 | /// returns a VanishingPolynomial of coset `H = h`. 21 | pub fn new(offset: F, dim_h: u64) -> Self { 22 | let order_h = 1 << dim_h; 23 | let vp = VanishingPolynomial { 24 | constant_term: offset.pow([order_h]), 25 | dim_h, 26 | order_h, 27 | }; 28 | vp 29 | } 30 | 31 | /// Evaluates the vanishing polynomial without generating the constraints. 32 | pub fn evaluate(&self, x: &F) -> F { 33 | let mut result = x.pow([self.order_h]); 34 | result -= &self.constant_term; 35 | result 36 | } 37 | 38 | /// Evaluates the constraints and just gives you the gadget for the result. 39 | /// Caution for use in holographic lincheck: The output has 2 entries in one 40 | /// matrix 41 | pub fn evaluate_constraints(&self, x: &FpVar) -> Result, SynthesisError> { 42 | if self.dim_h == 1 { 43 | let result = x.sub(x); 44 | return Ok(result); 45 | } 46 | 47 | let mut cur = x.square()?; 48 | for _ in 1..self.dim_h { 49 | cur.square_in_place()?; 50 | } 51 | cur -= &FpVar::Constant(self.constant_term); 52 | Ok(cur) 53 | } 54 | } 55 | 56 | #[cfg(test)] 57 | mod tests { 58 | use crate::{ 59 | alloc::AllocVar, fields::fp::FpVar, poly::domain::vanishing_poly::VanishingPolynomial, 60 | R1CSVar, 61 | }; 62 | use ark_relations::r1cs::ConstraintSystem; 63 | use ark_std::{test_rng, UniformRand}; 64 | use ark_test_curves::bls12_381::Fr; 65 | 66 | #[test] 67 | fn constraints_test() { 68 | let mut rng = test_rng(); 69 | let offset = Fr::rand(&mut rng); 70 | let cs = ConstraintSystem::new_ref(); 71 | let x = Fr::rand(&mut rng); 72 | let x_var = FpVar::new_witness(ns!(cs, "x_var"), || Ok(x)).unwrap(); 73 | let vp = VanishingPolynomial::new(offset, 12); 74 | let native = vp.evaluate(&x); 75 | let result_var = vp.evaluate_constraints(&x_var).unwrap(); 76 | assert!(cs.is_satisfied().unwrap()); 77 | assert_eq!(result_var.value().unwrap(), native); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ark-r1cs-std" 3 | version = "0.4.0" 4 | authors = [ "arkworks contributors" ] 5 | description = "A standard library for constraint system gadgets" 6 | homepage = "https://arkworks.rs" 7 | repository = "https://github.com/arkworks-rs/r1cs-std" 8 | documentation = "https://docs.rs/ark-r1cs-std/" 9 | keywords = ["zero-knowledge", "cryptography", "zkSNARK", "SNARK", "r1cs"] 10 | categories = ["cryptography"] 11 | include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] 12 | license = "MIT/Apache-2.0" 13 | edition = "2021" 14 | resolver = "2" 15 | 16 | [dependencies] 17 | ark-ff = { git = "https://github.com/Antalpha-Labs/algebra/" } 18 | ark-ec = { git = "https://github.com/Antalpha-Labs/algebra/" } 19 | ark-relations = { git = "https://github.com/Antalpha-Labs/snark/" } 20 | ark-std = { version = "0.4.0", default-features = false } 21 | 22 | derivative = { version = "2", features = ["use_core"] } 23 | tracing = { version = "0.1", default-features = false, features = [ "attributes" ] } 24 | num-bigint = { version = "0.4", default-features = false } 25 | num-traits = { version = "0.2", default-features = false } 26 | num-integer = { version = "0.1.44", default-features = false } 27 | 28 | [dev-dependencies] 29 | ark-test-curves = { version = "0.4.0", default-features = false, features = ["bls12_381_scalar_field", "bls12_381_curve", "mnt4_753_scalar_field"] } 30 | ark-poly = { git = "https://github.com/Antalpha-Labs/algebra/" } 31 | paste = "1.0" 32 | ark-bls12-377 = { version = "0.4.0", features = ["curve"], default-features = false } 33 | ark-bls12-381 = { version = "0.4.0", features = ["curve"], default-features = false } 34 | ark-mnt4-298 = { version = "0.4.0", features = ["curve"], default-features = false } 35 | ark-mnt4-753 = { version = "0.4.0", features = ["curve"], default-features = false } 36 | ark-mnt6-298 = { version = "0.4.0", default-features = false } 37 | ark-mnt6-753 = { version = "0.4.0", default-features = false } 38 | ark-pallas = { version = "0.4.0", features = ["curve"], default-features = false } 39 | ark-bn254 = { git = "https://github.com/Antalpha-Labs/algebra/", features = ["curve"], default-features = false } 40 | 41 | [features] 42 | default = ["std"] 43 | std = [ "ark-ff/std", "ark-relations/std", "ark-std/std", "num-bigint/std" ] 44 | parallel = [ "std", "ark-ff/parallel", "ark-std/parallel"] 45 | 46 | [[bench]] 47 | name = "emulated-bench" 48 | path = "benches/bench.rs" 49 | harness = false 50 | 51 | [profile.release] 52 | opt-level = 3 53 | lto = "thin" 54 | incremental = true 55 | panic = 'abort' 56 | 57 | [profile.bench] 58 | opt-level = 3 59 | debug = false 60 | rpath = false 61 | lto = "thin" 62 | incremental = true 63 | debug-assertions = false 64 | 65 | [profile.dev] 66 | opt-level = 0 67 | panic = 'abort' 68 | 69 | [profile.test] 70 | opt-level = 3 71 | lto = "thin" 72 | incremental = true 73 | debug-assertions = true 74 | debug = true 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

ark-r1cs-std

2 | 3 |

4 | 5 | 6 | 7 | 8 |

9 | 10 | The arkworks ecosystem consist of Rust libraries for designing and working with __zero knowledge succinct non-interactive arguments (zkSNARKs)__. This repository contains efficient implementations of constraint "gadgets" that enable checking common computations inside SNARKs, such as bit operations, finite field arithmetic, elliptic curve arithmetic, and pairings. 11 | 12 | This library is released under the MIT License and the Apache v2 License (see [License](#license)). 13 | 14 | **WARNING:** This is an academic proof-of-concept prototype, and in particular has not received careful code review. This implementation is NOT ready for production use. 15 | 16 | ## Build guide 17 | 18 | The library compiles on the `stable` toolchain of the Rust compiler. To install the latest version of Rust, first install `rustup` by following the instructions [here](https://rustup.rs/), or via your platform's package manager. Once `rustup` is installed, install the Rust toolchain by invoking: 19 | ```bash 20 | rustup install stable 21 | ``` 22 | 23 | After that, use `cargo`, the standard Rust build tool, to build the library: 24 | ```bash 25 | git clone https://github.com/arkworks-rs/r1cs-std.git 26 | cargo build --release 27 | ``` 28 | 29 | This library comes with unit tests for each of the provided crates. Run the tests with: 30 | ```bash 31 | cargo test 32 | ``` 33 | 34 | ## License 35 | 36 | This library is licensed under either of the following licenses, at your discretion. 37 | 38 | * Apache License Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 39 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 40 | 41 | Unless you explicitly state otherwise, any contribution submitted for inclusion in this library by you shall be dual licensed as above (as defined in the Apache v2 License), without any additional terms or conditions. 42 | 43 | ## Acknowledgements 44 | 45 | This work was supported by: 46 | a Google Faculty Award; 47 | the National Science Foundation; 48 | the UC Berkeley Center for Long-Term Cybersecurity; 49 | and donations from the Ethereum Foundation, the Interchain Foundation, and Qtum. 50 | 51 | An earlier version of this library was developed as part of the paper *"[ZEXE: Enabling Decentralized Private Computation][zexe]"*. 52 | 53 | [zexe]: https://ia.cr/2018/962 -------------------------------------------------------------------------------- /src/poly/polynomial/univariate/dense.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use ark_relations::r1cs::SynthesisError; 3 | 4 | use crate::fields::{fp::FpVar, FieldVar}; 5 | use ark_std::vec::Vec; 6 | 7 | /// Stores a polynomial in coefficient form, where coeffcient is represented by 8 | /// a list of `Fpvar`. 9 | pub struct DensePolynomialVar { 10 | /// The coefficient of `x^i` is stored at location `i` in `self.coeffs`. 11 | pub coeffs: Vec>, 12 | } 13 | 14 | impl DensePolynomialVar { 15 | /// Constructs a new polynomial from a list of coefficients. 16 | pub fn from_coefficients_slice(coeffs: &[FpVar]) -> Self { 17 | Self::from_coefficients_vec(coeffs.to_vec()) 18 | } 19 | 20 | /// Constructs a new polynomial from a list of coefficients. 21 | pub fn from_coefficients_vec(coeffs: Vec>) -> Self { 22 | Self { coeffs } 23 | } 24 | 25 | /// Evaluates `self` at the given `point` and just gives you the gadget for 26 | /// the result. Caution for use in holographic lincheck: The output has 27 | /// 2 entries in one matrix 28 | pub fn evaluate(&self, point: &FpVar) -> Result, SynthesisError> { 29 | let mut result: FpVar = FpVar::zero(); 30 | // current power of point 31 | let mut curr_pow_x: FpVar = FpVar::one(); 32 | for i in 0..self.coeffs.len() { 33 | let term = &curr_pow_x * &self.coeffs[i]; 34 | result += &term; 35 | curr_pow_x *= point; 36 | } 37 | 38 | Ok(result) 39 | } 40 | } 41 | 42 | #[cfg(test)] 43 | mod tests { 44 | use crate::{ 45 | alloc::AllocVar, fields::fp::FpVar, 46 | poly::polynomial::univariate::dense::DensePolynomialVar, R1CSVar, 47 | }; 48 | use ark_poly::{polynomial::univariate::DensePolynomial, DenseUVPolynomial, Polynomial}; 49 | use ark_relations::r1cs::ConstraintSystem; 50 | use ark_std::{test_rng, vec::Vec, UniformRand}; 51 | use ark_test_curves::bls12_381::Fr; 52 | 53 | #[test] 54 | fn test_evaluate() { 55 | let mut rng = test_rng(); 56 | for _ in 0..100 { 57 | let cs = ConstraintSystem::new_ref(); 58 | let poly: DensePolynomial = DensePolynomial::rand(10, &mut rng); 59 | let poly_var = { 60 | let coeff: Vec<_> = poly 61 | .coeffs 62 | .iter() 63 | .map(|&x| FpVar::new_witness(ns!(cs, "coeff"), || Ok(x)).unwrap()) 64 | .collect(); 65 | DensePolynomialVar::from_coefficients_vec(coeff) 66 | }; 67 | let point = Fr::rand(&mut rng); 68 | let point_var = FpVar::new_witness(ns!(cs, "point"), || Ok(point)).unwrap(); 69 | 70 | let expected = poly.evaluate(&point); 71 | let actual = poly_var.evaluate(&point_var).unwrap(); 72 | 73 | assert_eq!(actual.value().unwrap(), expected); 74 | assert!(cs.is_satisfied().unwrap()); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/fields/fp6_3over2.rs: -------------------------------------------------------------------------------- 1 | use crate::fields::{cubic_extension::*, fp2::*}; 2 | use ark_ff::{ 3 | fields::{fp6_3over2::*, Fp2}, 4 | CubicExtConfig, 5 | }; 6 | use ark_relations::r1cs::SynthesisError; 7 | use ark_std::ops::MulAssign; 8 | 9 | /// A sextic extension field constructed as the tower of a 10 | /// cubic extension over a quadratic extension field. 11 | /// This is the R1CS equivalent of `ark_ff::fp6_3over3::Fp6

`. 12 | pub type Fp6Var

= CubicExtVar::Fp2Config>, Fp6ConfigWrapper

>; 13 | 14 | impl CubicExtVarConfig> for Fp6ConfigWrapper

{ 15 | fn mul_base_field_vars_by_frob_coeff( 16 | c1: &mut Fp2Var, 17 | c2: &mut Fp2Var, 18 | power: usize, 19 | ) { 20 | *c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; 21 | *c2 *= Self::FROBENIUS_COEFF_C2[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; 22 | } 23 | } 24 | 25 | impl Fp6Var

{ 26 | /// Multiplies `self` by a sparse element which has `c0 == c2 == zero`. 27 | pub fn mul_by_0_c1_0(&self, c1: &Fp2Var) -> Result { 28 | // Karatsuba multiplication 29 | // v0 = a0 * b0 = 0 30 | 31 | // v1 = a1 * b1 32 | let v1 = &self.c1 * c1; 33 | 34 | // v2 = a2 * b2 = 0 35 | 36 | let a1_plus_a2 = &self.c1 + &self.c2; 37 | let b1_plus_b2 = c1.clone(); 38 | 39 | let a0_plus_a1 = &self.c0 + &self.c1; 40 | 41 | // c0 = (NONRESIDUE * ((a1 + a2)*(b1 + b2) - v1 - v2)) + v0 42 | // = NONRESIDUE * ((a1 + a2) * b1 - v1) 43 | let c0 = &(a1_plus_a2 * &b1_plus_b2 - &v1) * P::NONRESIDUE; 44 | 45 | // c1 = (a0 + a1) * (b0 + b1) - v0 - v1 + NONRESIDUE * v2 46 | // = (a0 + a1) * b1 - v1 47 | let c1 = a0_plus_a1 * c1 - &v1; 48 | // c2 = (a0 + a2) * (b0 + b2) - v0 - v2 + v1 49 | // = v1 50 | let c2 = v1; 51 | Ok(Self::new(c0, c1, c2)) 52 | } 53 | 54 | /// Multiplies `self` by a sparse element which has `c2 == zero`. 55 | pub fn mul_by_c0_c1_0( 56 | &self, 57 | c0: &Fp2Var, 58 | c1: &Fp2Var, 59 | ) -> Result { 60 | let v0 = &self.c0 * c0; 61 | let v1 = &self.c1 * c1; 62 | // v2 = 0. 63 | 64 | let a1_plus_a2 = &self.c1 + &self.c2; 65 | let a0_plus_a1 = &self.c0 + &self.c1; 66 | let a0_plus_a2 = &self.c0 + &self.c2; 67 | 68 | let b1_plus_b2 = c1.clone(); 69 | let b0_plus_b1 = c0 + c1; 70 | let b0_plus_b2 = c0.clone(); 71 | 72 | let c0 = (&a1_plus_a2 * &b1_plus_b2 - &v1) * P::NONRESIDUE + &v0; 73 | 74 | let c1 = a0_plus_a1 * &b0_plus_b1 - &v0 - &v1; 75 | 76 | let c2 = a0_plus_a2 * &b0_plus_b2 - &v0 + &v1; 77 | 78 | Ok(Self::new(c0, c1, c2)) 79 | } 80 | } 81 | 82 | impl MulAssign> for Fp6Var

{ 83 | fn mul_assign(&mut self, other: Fp2) { 84 | self.c0 *= other; 85 | self.c1 *= other; 86 | self.c2 *= other; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/uint/select.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::select::CondSelectGadget; 3 | 4 | impl CondSelectGadget 5 | for UInt 6 | { 7 | #[tracing::instrument(target = "r1cs", skip(cond, true_value, false_value))] 8 | fn conditionally_select( 9 | cond: &Boolean, 10 | true_value: &Self, 11 | false_value: &Self, 12 | ) -> Result { 13 | let selected_bits = true_value 14 | .bits 15 | .iter() 16 | .zip(&false_value.bits) 17 | .map(|(t, f)| cond.select(t, f)); 18 | let mut bits = [Boolean::FALSE; N]; 19 | for (result, new) in bits.iter_mut().zip(selected_bits) { 20 | *result = new?; 21 | } 22 | 23 | let value = cond.value().ok().and_then(|cond| { 24 | if cond { 25 | true_value.value().ok() 26 | } else { 27 | false_value.value().ok() 28 | } 29 | }); 30 | Ok(Self { bits, value }) 31 | } 32 | } 33 | 34 | #[cfg(test)] 35 | mod tests { 36 | use super::*; 37 | use crate::{ 38 | alloc::{AllocVar, AllocationMode}, 39 | prelude::EqGadget, 40 | uint::test_utils::{run_binary_exhaustive, run_binary_random}, 41 | }; 42 | use ark_ff::PrimeField; 43 | use ark_test_curves::bls12_381::Fr; 44 | 45 | fn uint_select( 46 | a: UInt, 47 | b: UInt, 48 | ) -> Result<(), SynthesisError> { 49 | let cs = a.cs().or(b.cs()); 50 | let both_constant = a.is_constant() && b.is_constant(); 51 | let expected_mode = if both_constant { 52 | AllocationMode::Constant 53 | } else { 54 | AllocationMode::Witness 55 | }; 56 | for cond in [true, false] { 57 | let expected = UInt::new_variable( 58 | cs.clone(), 59 | || Ok(if cond { a.value()? } else { b.value()? }), 60 | expected_mode, 61 | )?; 62 | let cond = Boolean::new_variable(cs.clone(), || Ok(cond), expected_mode)?; 63 | let computed = cond.select(&a, &b)?; 64 | 65 | assert_eq!(expected.value(), computed.value()); 66 | expected.enforce_equal(&computed)?; 67 | if !both_constant { 68 | assert!(cs.is_satisfied().unwrap()); 69 | } 70 | } 71 | Ok(()) 72 | } 73 | 74 | #[test] 75 | fn u8_select() { 76 | run_binary_exhaustive(uint_select::).unwrap() 77 | } 78 | 79 | #[test] 80 | fn u16_select() { 81 | run_binary_random::<1000, 16, _, _>(uint_select::).unwrap() 82 | } 83 | 84 | #[test] 85 | fn u32_select() { 86 | run_binary_random::<1000, 32, _, _>(uint_select::).unwrap() 87 | } 88 | 89 | #[test] 90 | fn u64_select() { 91 | run_binary_random::<1000, 64, _, _>(uint_select::).unwrap() 92 | } 93 | 94 | #[test] 95 | fn u128_select() { 96 | run_binary_random::<1000, 128, _, _>(uint_select::).unwrap() 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/pairing/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{convert::ToBytesGadget, prelude::*}; 2 | use ark_ec::pairing::Pairing; 3 | use ark_relations::r1cs::SynthesisError; 4 | use core::fmt::Debug; 5 | 6 | /// This module implements pairings for BLS12 bilinear groups. 7 | pub mod bls12; 8 | /// This module implements pairings for MNT4 bilinear groups. 9 | pub mod mnt4; 10 | /// This module implements pairings for MNT6 bilinear groups. 11 | pub mod mnt6; 12 | 13 | type BasePrimeField = <::BaseField as ark_ff::Field>::BasePrimeField; 14 | 15 | /// Specifies the constraints for computing a pairing in the yybilinear group 16 | /// `E`. 17 | pub trait PairingVar { 18 | /// An variable representing an element of `G1`. 19 | /// This is the R1CS equivalent of `E::G1Projective`. 20 | type G1Var: CurveVar>; 21 | 22 | /// An variable representing an element of `G2`. 23 | /// This is the R1CS equivalent of `E::G2Projective`. 24 | type G2Var: CurveVar>; 25 | 26 | /// An variable representing an element of `GT`. 27 | /// This is the R1CS equivalent of `E::GT`. 28 | type GTVar: FieldVar>; 29 | 30 | /// An variable representing cached precomputation that can speed up 31 | /// pairings computations. This is the R1CS equivalent of 32 | /// `E::G1Prepared`. 33 | type G1PreparedVar: ToBytesGadget> 34 | + AllocVar> 35 | + Clone 36 | + Debug; 37 | /// An variable representing cached precomputation that can speed up 38 | /// pairings computations. This is the R1CS equivalent of 39 | /// `E::G2Prepared`. 40 | type G2PreparedVar: ToBytesGadget> 41 | + AllocVar> 42 | + Clone 43 | + Debug; 44 | 45 | /// Computes a multi-miller loop between elements 46 | /// of `p` and `q`. 47 | fn miller_loop( 48 | p: &[Self::G1PreparedVar], 49 | q: &[Self::G2PreparedVar], 50 | ) -> Result; 51 | 52 | /// Computes a final exponentiation over `p`. 53 | fn final_exponentiation(p: &Self::GTVar) -> Result; 54 | 55 | /// Computes a pairing over `p` and `q`. 56 | #[tracing::instrument(target = "r1cs")] 57 | fn pairing( 58 | p: Self::G1PreparedVar, 59 | q: Self::G2PreparedVar, 60 | ) -> Result { 61 | let tmp = Self::miller_loop(&[p], &[q])?; 62 | Self::final_exponentiation(&tmp) 63 | } 64 | 65 | /// Computes a product of pairings over the elements in `p` and `q`. 66 | #[must_use] 67 | #[tracing::instrument(target = "r1cs")] 68 | fn product_of_pairings( 69 | p: &[Self::G1PreparedVar], 70 | q: &[Self::G2PreparedVar], 71 | ) -> Result { 72 | let miller_result = Self::miller_loop(p, q)?; 73 | Self::final_exponentiation(&miller_result) 74 | } 75 | 76 | /// Performs the precomputation to generate `Self::G1PreparedVar`. 77 | fn prepare_g1(q: &Self::G1Var) -> Result; 78 | 79 | /// Performs the precomputation to generate `Self::G2PreparedVar`. 80 | fn prepare_g2(q: &Self::G2Var) -> Result; 81 | } 82 | -------------------------------------------------------------------------------- /src/boolean/not.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::Field; 2 | use ark_relations::r1cs::SynthesisError; 3 | use ark_std::ops::Not; 4 | 5 | use super::Boolean; 6 | 7 | impl Boolean { 8 | fn _not(&self) -> Result { 9 | let mut result = self.clone(); 10 | result.not_in_place()?; 11 | Ok(result) 12 | } 13 | 14 | pub fn not_in_place(&mut self) -> Result<(), SynthesisError> { 15 | match *self { 16 | Boolean::Constant(ref mut c) => *c = !*c, 17 | Boolean::Var(ref mut v) => *v = v.not()?, 18 | } 19 | Ok(()) 20 | } 21 | } 22 | 23 | impl<'a, F: Field> Not for &'a Boolean { 24 | type Output = Boolean; 25 | /// Negates `self`. 26 | /// 27 | /// This *does not* create any new variables or constraints. 28 | /// ``` 29 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 30 | /// // We'll use the BLS12-381 scalar field for our constraints. 31 | /// use ark_test_curves::bls12_381::Fr; 32 | /// use ark_relations::r1cs::*; 33 | /// use ark_r1cs_std::prelude::*; 34 | /// 35 | /// let cs = ConstraintSystem::::new_ref(); 36 | /// 37 | /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; 38 | /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; 39 | /// 40 | /// (!&a).enforce_equal(&b)?; 41 | /// (!&b).enforce_equal(&a)?; 42 | /// 43 | /// (!&a).enforce_equal(&Boolean::FALSE)?; 44 | /// (!&b).enforce_equal(&Boolean::TRUE)?; 45 | /// 46 | /// assert!(cs.is_satisfied().unwrap()); 47 | /// # Ok(()) 48 | /// # } 49 | /// ``` 50 | #[tracing::instrument(target = "r1cs", skip(self))] 51 | fn not(self) -> Self::Output { 52 | self._not().unwrap() 53 | } 54 | } 55 | 56 | impl<'a, F: Field> Not for &'a mut Boolean { 57 | type Output = Boolean; 58 | 59 | #[tracing::instrument(target = "r1cs", skip(self))] 60 | fn not(self) -> Self::Output { 61 | self._not().unwrap() 62 | } 63 | } 64 | 65 | impl Not for Boolean { 66 | type Output = Boolean; 67 | 68 | #[tracing::instrument(target = "r1cs", skip(self))] 69 | fn not(self) -> Self::Output { 70 | self._not().unwrap() 71 | } 72 | } 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | use super::*; 77 | use crate::{ 78 | alloc::{AllocVar, AllocationMode}, 79 | boolean::test_utils::run_unary_exhaustive, 80 | prelude::EqGadget, 81 | R1CSVar, 82 | }; 83 | use ark_test_curves::bls12_381::Fr; 84 | 85 | #[test] 86 | fn not() { 87 | run_unary_exhaustive::(|a| { 88 | let cs = a.cs(); 89 | let computed = !&a; 90 | let expected_mode = if a.is_constant() { 91 | AllocationMode::Constant 92 | } else { 93 | AllocationMode::Witness 94 | }; 95 | let expected = Boolean::new_variable(cs.clone(), || Ok(!a.value()?), expected_mode)?; 96 | assert_eq!(expected.value(), computed.value()); 97 | expected.enforce_equal(&computed)?; 98 | if !a.is_constant() { 99 | assert!(cs.is_satisfied().unwrap()); 100 | } 101 | Ok(()) 102 | }) 103 | .unwrap() 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | //! This crate implements common "gadgets" that make 3 | //! programming rank-1 constraint systems easier. 4 | #![deny( 5 | warnings, 6 | unused, 7 | future_incompatible, 8 | nonstandard_style, 9 | rust_2018_idioms 10 | )] 11 | #![allow(clippy::op_ref)] 12 | 13 | #[macro_use] 14 | extern crate ark_std; 15 | 16 | #[macro_use] 17 | extern crate ark_ff; 18 | 19 | #[macro_use] 20 | extern crate ark_relations; 21 | 22 | #[doc(hidden)] 23 | #[macro_use] 24 | extern crate derivative; 25 | 26 | /// Some utility macros for making downstream impls easier. 27 | #[macro_use] 28 | pub mod macros; 29 | 30 | pub(crate) use ark_std::vec::Vec; 31 | 32 | #[doc(hidden)] 33 | pub mod r1cs_var; 34 | pub use r1cs_var::*; 35 | 36 | /// This module contains `Boolean`, an R1CS equivalent of the `bool` type. 37 | pub mod boolean; 38 | 39 | /// Finite field arithmetic. 40 | pub mod fields; 41 | 42 | /// Implementations of elliptic curve group arithmetic for popular curve models. 43 | pub mod groups; 44 | 45 | /// Gadgets for computing pairings in bilinear groups. 46 | pub mod pairing; 47 | 48 | /// Utilities for allocating new variables in a constraint system. 49 | pub mod alloc; 50 | 51 | /// Utilities for comparing variables. 52 | pub mod cmp; 53 | 54 | /// Utilities for converting variables to other kinds of variables. 55 | pub mod convert; 56 | 57 | /// Utilities for checking equality of variables. 58 | pub mod eq; 59 | 60 | /// Definitions of polynomial variables over finite fields. 61 | pub mod poly; 62 | 63 | /// Contains traits for conditionally selecting a variable from a 64 | /// list of variables. 65 | pub mod select; 66 | 67 | #[cfg(test)] 68 | pub(crate) mod test_utils; 69 | 70 | /// This module contains `UInt8`, a R1CS equivalent of the `u8` type. 71 | pub mod uint8; 72 | /// This module contains a macro for generating `UIntN` types, which are R1CS 73 | /// equivalents of `N`-bit unsigned integers. 74 | #[macro_use] 75 | pub mod uint; 76 | 77 | pub mod uint16 { 78 | pub type UInt16 = super::uint::UInt<16, u16, F>; 79 | } 80 | pub mod uint32 { 81 | pub type UInt32 = super::uint::UInt<32, u32, F>; 82 | } 83 | pub mod uint64 { 84 | pub type UInt64 = super::uint::UInt<64, u64, F>; 85 | } 86 | pub mod uint128 { 87 | pub type UInt128 = super::uint::UInt<128, u128, F>; 88 | } 89 | 90 | #[allow(missing_docs)] 91 | pub mod prelude { 92 | pub use crate::{ 93 | alloc::*, 94 | boolean::Boolean, 95 | convert::{ToBitsGadget, ToBytesGadget}, 96 | eq::*, 97 | fields::{FieldOpsBounds, FieldVar}, 98 | groups::{CurveVar, GroupOpsBounds}, 99 | pairing::PairingVar, 100 | select::*, 101 | uint128::UInt128, 102 | uint16::UInt16, 103 | uint32::UInt32, 104 | uint64::UInt64, 105 | uint8::UInt8, 106 | R1CSVar, 107 | }; 108 | } 109 | 110 | /// A utility trait to convert `Self` to `Result 111 | pub trait Assignment { 112 | /// Converts `self` to `Result`. 113 | fn get(self) -> Result; 114 | } 115 | 116 | impl Assignment for Option { 117 | fn get(self) -> Result { 118 | self.ok_or(ark_relations::r1cs::SynthesisError::AssignmentMissing) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/uint/add/wrapping.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use ark_relations::r1cs::SynthesisError; 3 | 4 | use crate::uint::*; 5 | use crate::R1CSVar; 6 | 7 | impl UInt { 8 | /// Compute `*self = self.wrapping_add(other)`. 9 | pub fn wrapping_add_in_place(&mut self, other: &Self) { 10 | let result = Self::wrapping_add_many(&[self.clone(), other.clone()]).unwrap(); 11 | *self = result; 12 | } 13 | 14 | /// Compute `self.wrapping_add(other)`. 15 | pub fn wrapping_add(&self, other: &Self) -> Self { 16 | let mut result = self.clone(); 17 | result.wrapping_add_in_place(other); 18 | result 19 | } 20 | 21 | /// Perform wrapping addition of `operands`. 22 | /// Computes `operands[0].wrapping_add(operands[1]).wrapping_add(operands[2])...`. 23 | /// 24 | /// The user must ensure that overflow does not occur. 25 | #[tracing::instrument(target = "r1cs", skip(operands))] 26 | pub fn wrapping_add_many(operands: &[Self]) -> Result 27 | where 28 | F: PrimeField, 29 | { 30 | let (mut sum_bits, value) = Self::add_many_helper(operands, |a, b| a.wrapping_add(&b))?; 31 | if operands.is_constant() { 32 | // If all operands are constant, then the result is also constant. 33 | // In this case, we can return early. 34 | Ok(UInt::constant(value.unwrap())) 35 | } else { 36 | sum_bits.truncate(N); 37 | Ok(UInt { 38 | bits: sum_bits.try_into().unwrap(), 39 | value, 40 | }) 41 | } 42 | } 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | use super::*; 48 | use crate::{ 49 | alloc::{AllocVar, AllocationMode}, 50 | prelude::EqGadget, 51 | uint::test_utils::{run_binary_exhaustive, run_binary_random}, 52 | R1CSVar, 53 | }; 54 | use ark_ff::PrimeField; 55 | use ark_test_curves::bls12_381::Fr; 56 | 57 | fn uint_wrapping_add( 58 | a: UInt, 59 | b: UInt, 60 | ) -> Result<(), SynthesisError> { 61 | let cs = a.cs().or(b.cs()); 62 | let both_constant = a.is_constant() && b.is_constant(); 63 | let computed = a.wrapping_add(&b); 64 | let expected_mode = if both_constant { 65 | AllocationMode::Constant 66 | } else { 67 | AllocationMode::Witness 68 | }; 69 | let expected = UInt::new_variable( 70 | cs.clone(), 71 | || Ok(a.value()?.wrapping_add(&b.value()?)), 72 | expected_mode, 73 | )?; 74 | assert_eq!(expected.value(), computed.value()); 75 | expected.enforce_equal(&computed)?; 76 | if !both_constant { 77 | assert!(cs.is_satisfied().unwrap()); 78 | } 79 | Ok(()) 80 | } 81 | 82 | #[test] 83 | fn u8_wrapping_add() { 84 | run_binary_exhaustive(uint_wrapping_add::).unwrap() 85 | } 86 | 87 | #[test] 88 | fn u16_wrapping_add() { 89 | run_binary_random::<1000, 16, _, _>(uint_wrapping_add::).unwrap() 90 | } 91 | 92 | #[test] 93 | fn u32_wrapping_add() { 94 | run_binary_random::<1000, 32, _, _>(uint_wrapping_add::).unwrap() 95 | } 96 | 97 | #[test] 98 | fn u64_wrapping_add() { 99 | run_binary_random::<1000, 64, _, _>(uint_wrapping_add::).unwrap() 100 | } 101 | 102 | #[test] 103 | fn u128_wrapping_add() { 104 | run_binary_random::<1000, 128, _, _>(uint_wrapping_add::).unwrap() 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/boolean/cmp.rs: -------------------------------------------------------------------------------- 1 | use crate::cmp::CmpGadget; 2 | 3 | use super::*; 4 | use ark_ff::PrimeField; 5 | 6 | impl CmpGadget for Boolean { 7 | fn is_ge(&self, other: &Self) -> Result, SynthesisError> { 8 | // a | b | (a | !b) | a >= b 9 | // --|---|--------|-------- 10 | // 0 | 0 | 1 | 1 11 | // 1 | 0 | 1 | 1 12 | // 0 | 1 | 0 | 0 13 | // 1 | 1 | 1 | 1 14 | Ok(self | &(!other)) 15 | } 16 | } 17 | 18 | impl Boolean { 19 | /// Enforces that `bits`, when interpreted as a integer, is less than 20 | /// `F::characteristic()`, That is, interpret bits as a little-endian 21 | /// integer, and enforce that this integer is "in the field Z_p", where 22 | /// `p = F::characteristic()` . 23 | #[tracing::instrument(target = "r1cs")] 24 | pub fn enforce_in_field_le(bits: &[Self]) -> Result<(), SynthesisError> { 25 | // `bits` < F::characteristic() <==> `bits` <= F::characteristic() -1 26 | let mut b = F::characteristic().to_vec(); 27 | assert_eq!(b[0] % 2, 1); 28 | b[0] -= 1; // This works, because the LSB is one, so there's no borrows. 29 | let run = Self::enforce_smaller_or_equal_than_le(bits, b)?; 30 | 31 | // We should always end in a "run" of zeros, because 32 | // the characteristic is an odd prime. So, this should 33 | // be empty. 34 | assert!(run.is_empty()); 35 | 36 | Ok(()) 37 | } 38 | 39 | /// Enforces that `bits` is less than or equal to `element`, 40 | /// when both are interpreted as (little-endian) integers. 41 | #[tracing::instrument(target = "r1cs", skip(element))] 42 | pub fn enforce_smaller_or_equal_than_le( 43 | bits: &[Self], 44 | element: impl AsRef<[u64]>, 45 | ) -> Result, SynthesisError> { 46 | let b: &[u64] = element.as_ref(); 47 | 48 | let mut bits_iter = bits.iter().rev(); // Iterate in big-endian 49 | 50 | // Runs of ones in r 51 | let mut last_run = Boolean::TRUE; 52 | let mut current_run = vec![]; 53 | 54 | let mut element_num_bits = 0; 55 | for _ in BitIteratorBE::without_leading_zeros(b) { 56 | element_num_bits += 1; 57 | } 58 | 59 | if bits.len() > element_num_bits { 60 | let mut or_result = Boolean::FALSE; 61 | for should_be_zero in &bits[element_num_bits..] { 62 | or_result |= should_be_zero; 63 | let _ = bits_iter.next().unwrap(); 64 | } 65 | or_result.enforce_equal(&Boolean::FALSE)?; 66 | } 67 | 68 | for (b, a) in BitIteratorBE::without_leading_zeros(b).zip(bits_iter.by_ref()) { 69 | if b { 70 | // This is part of a run of ones. 71 | current_run.push(a.clone()); 72 | } else { 73 | if !current_run.is_empty() { 74 | // This is the start of a run of zeros, but we need 75 | // to k-ary AND against `last_run` first. 76 | 77 | current_run.push(last_run.clone()); 78 | last_run = Self::kary_and(¤t_run)?; 79 | current_run.truncate(0); 80 | } 81 | 82 | // If `last_run` is true, `a` must be false, or it would 83 | // not be in the field. 84 | // 85 | // If `last_run` is false, `a` can be true or false. 86 | // 87 | // Ergo, at least one of `last_run` and `a` must be false. 88 | Self::enforce_kary_nand(&[last_run.clone(), a.clone()])?; 89 | } 90 | } 91 | assert!(bits_iter.next().is_none()); 92 | 93 | Ok(current_run) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/fields/emulated_fp/params.rs: -------------------------------------------------------------------------------- 1 | use super::NonNativeFieldConfig; 2 | 3 | /// Obtain the parameters from a `ConstraintSystem`'s cache or generate a new 4 | /// one 5 | #[must_use] 6 | pub const fn get_params( 7 | target_field_size: usize, 8 | base_field_size: usize, 9 | optimization_type: OptimizationType, 10 | ) -> NonNativeFieldConfig { 11 | let (num_of_limbs, limb_size) = 12 | find_parameters(base_field_size, target_field_size, optimization_type); 13 | NonNativeFieldConfig { 14 | num_limbs: num_of_limbs, 15 | bits_per_limb: limb_size, 16 | } 17 | } 18 | 19 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 20 | /// The type of optimization target for the parameters searching 21 | pub enum OptimizationType { 22 | /// Optimized for constraints 23 | Constraints, 24 | /// Optimized for weight 25 | Weight, 26 | } 27 | 28 | /// A function to search for parameters for emulated field gadgets 29 | pub const fn find_parameters( 30 | base_field_prime_length: usize, 31 | target_field_prime_bit_length: usize, 32 | optimization_type: OptimizationType, 33 | ) -> (usize, usize) { 34 | let mut found = false; 35 | let mut min_cost = 0usize; 36 | let mut min_cost_limb_size = 0usize; 37 | let mut min_cost_num_of_limbs = 0usize; 38 | 39 | let surfeit = 10; 40 | let mut max_limb_size = (base_field_prime_length - 1 - surfeit - 1) / 2 - 1; 41 | if max_limb_size > target_field_prime_bit_length { 42 | max_limb_size = target_field_prime_bit_length; 43 | } 44 | let mut limb_size = 1; 45 | 46 | while limb_size <= max_limb_size { 47 | let num_of_limbs = (target_field_prime_bit_length + limb_size - 1) / limb_size; 48 | 49 | let group_size = 50 | (base_field_prime_length - 1 - surfeit - 1 - 1 - limb_size + limb_size - 1) / limb_size; 51 | let num_of_groups = (2 * num_of_limbs - 1 + group_size - 1) / group_size; 52 | 53 | let mut this_cost = 0; 54 | 55 | match optimization_type { 56 | OptimizationType::Constraints => { 57 | this_cost += 2 * num_of_limbs - 1; 58 | }, 59 | OptimizationType::Weight => { 60 | this_cost += 6 * num_of_limbs * num_of_limbs; 61 | }, 62 | }; 63 | 64 | match optimization_type { 65 | OptimizationType::Constraints => { 66 | this_cost += target_field_prime_bit_length; // allocation of k 67 | this_cost += target_field_prime_bit_length + num_of_limbs; // allocation of r 68 | // this_cost += 2 * num_of_limbs - 1; // compute kp 69 | this_cost += num_of_groups + (num_of_groups - 1) * (limb_size * 2 + surfeit) + 1; 70 | // equality check 71 | }, 72 | OptimizationType::Weight => { 73 | this_cost += target_field_prime_bit_length * 3 + target_field_prime_bit_length; // allocation of k 74 | this_cost += target_field_prime_bit_length * 3 75 | + target_field_prime_bit_length 76 | + num_of_limbs; // allocation of r 77 | this_cost += num_of_limbs * num_of_limbs + 2 * (2 * num_of_limbs - 1); // compute kp 78 | this_cost += num_of_limbs 79 | + num_of_groups 80 | + 6 * num_of_groups 81 | + (num_of_groups - 1) * (2 * limb_size + surfeit) * 4 82 | + 2; // equality check 83 | }, 84 | }; 85 | 86 | if !found || this_cost < min_cost { 87 | found = true; 88 | min_cost = this_cost; 89 | min_cost_limb_size = limb_size; 90 | min_cost_num_of_limbs = num_of_limbs; 91 | } 92 | 93 | limb_size += 1; 94 | } 95 | 96 | (min_cost_num_of_limbs, min_cost_limb_size) 97 | } 98 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for considering making contributions to `arkworks-rs/r1cs-std`! 4 | 5 | Contributing to this repo can be done in several forms, such as participating in discussion or proposing code changes. 6 | To ensure a smooth workflow for all contributors, the following general procedure for contributing has been established: 7 | 8 | 1) Either open or find an issue you'd like to help with 9 | 2) Participate in thoughtful discussion on that issue 10 | 3) If you would like to contribute: 11 | * If the issue is a feature proposal, ensure that the proposal has been accepted 12 | * Ensure that nobody else has already begun working on this issue. 13 | If they have, please try to contact them to collaborate 14 | * If nobody has been assigned for the issue and you would like to work on it, make a comment on the issue to inform the community of your intentions to begin work. (So we can avoid duplication of efforts) 15 | * We suggest using standard Github best practices for contributing: fork the repo, branch from the HEAD of `main`, make some commits on your branch, and submit a PR from the branch to `main`. 16 | More detail on this is below 17 | * Be sure to include a relevant change log entry in the Pending section of CHANGELOG.md (see file for log format) 18 | * If the change is breaking, we may add migration instructions. 19 | 20 | Note that for very small or clear problems (such as typos), or well isolated improvements, it is not required to an open issue to submit a PR. 21 | But be aware that for more complex problems/features touching multiple parts of the codebase, if a PR is opened before an adequate design discussion has taken place in a github issue, that PR runs a larger likelihood of being rejected. 22 | 23 | Looking for a good place to start contributing? How about checking out some good first issues 24 | 25 | ## Branch Structure 26 | 27 | `r1cs-std` has its default branch as `main`, which is where PRs are merged into. Releases will be periodically made, on no set schedule. 28 | All other branches should be assumed to be miscellaneous feature development branches. 29 | 30 | All downstream users of the library should be using tagged versions of the library pulled from cargo. 31 | 32 | ## How to work on a fork 33 | Please skip this section if you're familiar with contributing to opensource github projects. 34 | 35 | First fork the repo from the github UI, and clone it locally. 36 | Then in the repo, you want to add the repo you forked from as a new remote. You do this as: 37 | ```bash 38 | git remote add upstream git@github.com:arkworks-rs/r1cs-std.git 39 | ``` 40 | 41 | Then the way you make code contributions is to first think of a branch name that describes your change. 42 | Then do the following: 43 | ```bash 44 | git checkout main 45 | git pull upstream main 46 | git checkout -b $NEW_BRANCH_NAME 47 | ``` 48 | and then work as normal on that branch, and pull request to upstream master when you're done =) 49 | 50 | ## Updating documentation 51 | 52 | All PRs should aim to leave the code more documented than it started with. 53 | Please don't assume that its easy to infer what the code is doing, 54 | as that is almost always not the case for these complex protocols. 55 | (Even when you understand the paper!) 56 | 57 | Its often very useful to describe what is the high level view of what a code block is doing, 58 | and either refer to the relevant section of a paper or include a short proof/argument for why it makes sense before the actual logic. 59 | 60 | ## Performance improvements 61 | 62 | All performance improvements should be accompanied with benchmarks improving, or otherwise have it be clear that things have improved. 63 | For some areas of the codebase, performance roughly follows the number of field multiplications, but there are also many areas where 64 | hard to predict low level system effects such as cache locality and superscalar operations become important for performance. 65 | Thus performance can often become very non-intuitive / diverge from minimizing the number of arithmetic operations. -------------------------------------------------------------------------------- /src/uint/add/saturating.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use ark_relations::r1cs::SynthesisError; 3 | 4 | use crate::uint::*; 5 | use crate::{boolean::Boolean, R1CSVar}; 6 | 7 | impl UInt { 8 | /// Compute `*self = self.wrapping_add(other)`. 9 | pub fn saturating_add_in_place(&mut self, other: &Self) { 10 | let result = Self::saturating_add_many(&[self.clone(), other.clone()]).unwrap(); 11 | *self = result; 12 | } 13 | 14 | /// Compute `self.wrapping_add(other)`. 15 | pub fn saturating_add(&self, other: &Self) -> Self { 16 | let mut result = self.clone(); 17 | result.saturating_add_in_place(other); 18 | result 19 | } 20 | 21 | /// Perform wrapping addition of `operands`. 22 | /// Computes `operands[0].wrapping_add(operands[1]).wrapping_add(operands[2])...`. 23 | /// 24 | /// The user must ensure that overflow does not occur. 25 | #[tracing::instrument(target = "r1cs", skip(operands))] 26 | pub fn saturating_add_many(operands: &[Self]) -> Result 27 | where 28 | F: PrimeField, 29 | { 30 | let (sum_bits, value) = Self::add_many_helper(operands, |a, b| a.saturating_add(b))?; 31 | if operands.is_constant() { 32 | // If all operands are constant, then the result is also constant. 33 | // In this case, we can return early. 34 | Ok(UInt::constant(value.unwrap())) 35 | } else if sum_bits.len() == N { 36 | // No overflow occurred. 37 | Ok(UInt::from_bits_le(&sum_bits)) 38 | } else { 39 | // Split the sum into the bottom `N` bits and the top bits. 40 | let (bottom_bits, top_bits) = sum_bits.split_at(N); 41 | 42 | // Construct a candidate result assuming that no overflow occurred. 43 | let bits = TryFrom::try_from(bottom_bits.to_vec()).unwrap(); 44 | let candidate_result = UInt { bits, value }; 45 | 46 | // Check if any of the top bits is set. 47 | // If any of them is set, then overflow occurred. 48 | let overflow_occurred = Boolean::kary_or(&top_bits)?; 49 | 50 | // If overflow occurred, return the maximum value. 51 | overflow_occurred.select(&Self::MAX, &candidate_result) 52 | } 53 | } 54 | } 55 | 56 | #[cfg(test)] 57 | mod tests { 58 | use super::*; 59 | use crate::{ 60 | alloc::{AllocVar, AllocationMode}, 61 | prelude::EqGadget, 62 | uint::test_utils::{run_binary_exhaustive, run_binary_random}, 63 | R1CSVar, 64 | }; 65 | use ark_ff::PrimeField; 66 | use ark_test_curves::bls12_381::Fr; 67 | 68 | fn uint_saturating_add( 69 | a: UInt, 70 | b: UInt, 71 | ) -> Result<(), SynthesisError> { 72 | let cs = a.cs().or(b.cs()); 73 | let both_constant = a.is_constant() && b.is_constant(); 74 | let computed = a.saturating_add(&b); 75 | let expected_mode = if both_constant { 76 | AllocationMode::Constant 77 | } else { 78 | AllocationMode::Witness 79 | }; 80 | let expected = UInt::new_variable( 81 | cs.clone(), 82 | || Ok(a.value()?.saturating_add(b.value()?)), 83 | expected_mode, 84 | )?; 85 | assert_eq!(expected.value(), computed.value()); 86 | expected.enforce_equal(&computed)?; 87 | if !both_constant { 88 | assert!(cs.is_satisfied().unwrap()); 89 | } 90 | Ok(()) 91 | } 92 | 93 | #[test] 94 | fn u8_saturating_add() { 95 | run_binary_exhaustive(uint_saturating_add::).unwrap() 96 | } 97 | 98 | #[test] 99 | fn u16_saturating_add() { 100 | run_binary_random::<1000, 16, _, _>(uint_saturating_add::).unwrap() 101 | } 102 | 103 | #[test] 104 | fn u32_saturating_add() { 105 | run_binary_random::<1000, 32, _, _>(uint_saturating_add::).unwrap() 106 | } 107 | 108 | #[test] 109 | fn u64_saturating_add() { 110 | run_binary_random::<1000, 64, _, _>(uint_saturating_add::).unwrap() 111 | } 112 | 113 | #[test] 114 | fn u128_saturating_add() { 115 | run_binary_random::<1000, 128, _, _>(uint_saturating_add::).unwrap() 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/select.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use ark_ff::Field; 3 | use ark_relations::r1cs::SynthesisError; 4 | use ark_std::vec::Vec; 5 | /// Generates constraints for selecting between one of two values. 6 | pub trait CondSelectGadget: Sized + Clone { 7 | /// If `cond == &Boolean::TRUE`, then this returns `true_value`; else, 8 | /// returns `false_value`. 9 | /// 10 | /// # Note 11 | /// `Self::conditionally_select(cond, true_value, false_value)?` can be more 12 | /// succinctly written as `cond.select(&true_value, &false_value)?`. 13 | fn conditionally_select( 14 | cond: &Boolean, 15 | true_value: &Self, 16 | false_value: &Self, 17 | ) -> Result; 18 | 19 | /// Returns an element of `values` whose index in represented by `position`. 20 | /// `position` is an array of boolean that represents an unsigned integer in 21 | /// big endian order. 22 | /// 23 | /// # Example 24 | /// To get the 6th element of `values`, convert unsigned integer 6 (`0b110`) 25 | /// to `position = [True, True, False]`, 26 | /// and call `conditionally_select_power_of_two_vector(position, values)`. 27 | fn conditionally_select_power_of_two_vector( 28 | position: &[Boolean], 29 | values: &[Self], 30 | ) -> Result { 31 | let m = values.len(); 32 | let n = position.len(); 33 | 34 | // Assert m is a power of 2, and n = log(m) 35 | assert!(m.is_power_of_two()); 36 | assert_eq!(1 << n, m); 37 | 38 | let mut cur_mux_values = values.to_vec(); 39 | 40 | // Traverse the evaluation tree from bottom to top in level order traversal. 41 | // This is method 5.1 from https://github.com/mir-protocol/r1cs-workshop/blob/master/workshop.pdf 42 | // TODO: Add method 5.2/5.3 43 | for i in 0..n { 44 | // Size of current layer. 45 | let cur_size = 1 << (n - i); 46 | assert_eq!(cur_mux_values.len(), cur_size); 47 | 48 | let mut next_mux_values = Vec::new(); 49 | for j in (0..cur_size).step_by(2) { 50 | let cur = Self::conditionally_select( 51 | &position[n - 1 - i], 52 | // true case 53 | &cur_mux_values[j + 1], 54 | // false case 55 | &cur_mux_values[j], 56 | )?; 57 | next_mux_values.push(cur); 58 | } 59 | cur_mux_values = next_mux_values; 60 | } 61 | 62 | Ok(cur_mux_values[0].clone()) 63 | } 64 | } 65 | 66 | /// Performs a lookup in a 4-element table using two bits. 67 | pub trait TwoBitLookupGadget: Sized { 68 | /// The type of values being looked up. 69 | type TableConstant; 70 | 71 | /// Interprets the slice `bits` as a two-bit integer `b = bits[0] + (bits[1] 72 | /// << 1)`, and then outputs `constants[b]`. 73 | /// 74 | /// For example, if `bits == [0, 1]`, and `constants == [0, 1, 2, 3]`, this 75 | /// method should output a variable corresponding to `2`. 76 | /// 77 | /// # Panics 78 | /// 79 | /// This method panics if `bits.len() != 2` or `constants.len() != 4`. 80 | fn two_bit_lookup( 81 | bits: &[Boolean], 82 | constants: &[Self::TableConstant], 83 | ) -> Result; 84 | } 85 | 86 | /// Uses three bits to perform a lookup into a table, where the last bit 87 | /// conditionally negates the looked-up value. 88 | pub trait ThreeBitCondNegLookupGadget: Sized { 89 | /// The type of values being looked up. 90 | type TableConstant; 91 | 92 | /// Interprets the slice `bits` as a two-bit integer `b = bits[0] + (bits[1] 93 | /// << 1)`, and then outputs `constants[b] * c`, where `c = if bits[2] { 94 | /// -1 } else { 1 };`. 95 | /// 96 | /// That is, `bits[2]` conditionally negates the looked-up value. 97 | /// 98 | /// For example, if `bits == [1, 0, 1]`, and `constants == [0, 1, 2, 3]`, 99 | /// this method should output a variable corresponding to `-1`. 100 | /// 101 | /// # Panics 102 | /// 103 | /// This method panics if `bits.len() != 3` or `constants.len() != 4`. 104 | fn three_bit_cond_neg_lookup( 105 | bits: &[Boolean], 106 | b0b1: &Boolean, 107 | constants: &[Self::TableConstant], 108 | ) -> Result; 109 | } 110 | -------------------------------------------------------------------------------- /src/uint/not.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::Field; 2 | use ark_relations::r1cs::SynthesisError; 3 | use ark_std::ops::Not; 4 | 5 | use super::*; 6 | 7 | impl UInt { 8 | fn _not(&self) -> Result { 9 | let mut result = self.clone(); 10 | result._not_in_place()?; 11 | Ok(result) 12 | } 13 | 14 | fn _not_in_place(&mut self) -> Result<(), SynthesisError> { 15 | for a in &mut self.bits { 16 | a.not_in_place()?; 17 | } 18 | self.value = self.value.map(Not::not); 19 | Ok(()) 20 | } 21 | } 22 | 23 | impl<'a, const N: usize, T: PrimUInt, F: Field> Not for &'a UInt { 24 | type Output = UInt; 25 | /// Outputs `!self`. 26 | /// 27 | /// If `self` is a constant, then this method *does not* create any constraints or variables. 28 | /// 29 | /// ``` 30 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 31 | /// // We'll use the BLS12-381 scalar field for our constraints. 32 | /// use ark_test_curves::bls12_381::Fr; 33 | /// use ark_relations::r1cs::*; 34 | /// use ark_r1cs_std::prelude::*; 35 | /// 36 | /// let cs = ConstraintSystem::::new_ref(); 37 | /// let a = UInt8::new_witness(cs.clone(), || Ok(2))?; 38 | /// let b = UInt8::new_witness(cs.clone(), || Ok(!2))?; 39 | /// 40 | /// (!a).enforce_equal(&b)?; 41 | /// assert!(cs.is_satisfied().unwrap()); 42 | /// # Ok(()) 43 | /// # } 44 | /// ``` 45 | #[tracing::instrument(target = "r1cs", skip(self))] 46 | fn not(self) -> Self::Output { 47 | self._not().unwrap() 48 | } 49 | } 50 | 51 | impl<'a, const N: usize, T: PrimUInt, F: Field> Not for UInt { 52 | type Output = UInt; 53 | 54 | /// Outputs `!self`. 55 | /// 56 | /// If `self` is a constant, then this method *does not* create any constraints or variables. 57 | /// 58 | /// ``` 59 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 60 | /// // We'll use the BLS12-381 scalar field for our constraints. 61 | /// use ark_test_curves::bls12_381::Fr; 62 | /// use ark_relations::r1cs::*; 63 | /// use ark_r1cs_std::prelude::*; 64 | /// 65 | /// let cs = ConstraintSystem::::new_ref(); 66 | /// let a = UInt8::new_witness(cs.clone(), || Ok(2))?; 67 | /// let b = UInt8::new_witness(cs.clone(), || Ok(!2))?; 68 | /// 69 | /// (!a).enforce_equal(&b)?; 70 | /// assert!(cs.is_satisfied().unwrap()); 71 | /// # Ok(()) 72 | /// # } 73 | /// ``` 74 | #[tracing::instrument(target = "r1cs", skip(self))] 75 | fn not(mut self) -> Self::Output { 76 | self._not_in_place().unwrap(); 77 | self 78 | } 79 | } 80 | 81 | #[cfg(test)] 82 | mod tests { 83 | use super::*; 84 | use crate::{ 85 | alloc::{AllocVar, AllocationMode}, 86 | prelude::EqGadget, 87 | uint::test_utils::{run_unary_exhaustive, run_unary_random}, 88 | R1CSVar, 89 | }; 90 | use ark_ff::PrimeField; 91 | use ark_test_curves::bls12_381::Fr; 92 | 93 | fn uint_not( 94 | a: UInt, 95 | ) -> Result<(), SynthesisError> { 96 | let cs = a.cs(); 97 | let computed = !&a; 98 | let expected_mode = if a.is_constant() { 99 | AllocationMode::Constant 100 | } else { 101 | AllocationMode::Witness 102 | }; 103 | let expected = 104 | UInt::::new_variable(cs.clone(), || Ok(!a.value()?), expected_mode)?; 105 | assert_eq!(expected.value(), computed.value()); 106 | expected.enforce_equal(&computed)?; 107 | if !a.is_constant() { 108 | assert!(cs.is_satisfied().unwrap()); 109 | } 110 | Ok(()) 111 | } 112 | 113 | #[test] 114 | fn u8_not() { 115 | run_unary_exhaustive(uint_not::).unwrap() 116 | } 117 | 118 | #[test] 119 | fn u16_not() { 120 | run_unary_random::<1000, 16, _, _>(uint_not::).unwrap() 121 | } 122 | 123 | #[test] 124 | fn u32_not() { 125 | run_unary_random::<1000, 32, _, _>(uint_not::).unwrap() 126 | } 127 | 128 | #[test] 129 | fn u64_not() { 130 | run_unary_random::<1000, 64, _, _>(uint_not::).unwrap() 131 | } 132 | 133 | #[test] 134 | fn u128() { 135 | run_unary_random::<1000, 128, _, _>(uint_not::).unwrap() 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/boolean/xor.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::Field; 2 | use ark_relations::r1cs::SynthesisError; 3 | use ark_std::{ops::BitXor, ops::BitXorAssign}; 4 | 5 | use super::Boolean; 6 | 7 | impl Boolean { 8 | fn _xor(&self, other: &Self) -> Result { 9 | use Boolean::*; 10 | match (self, other) { 11 | (&Constant(false), x) | (x, &Constant(false)) => Ok(x.clone()), 12 | (&Constant(true), x) | (x, &Constant(true)) => Ok(!x), 13 | (Var(ref x), Var(ref y)) => Ok(Var(x.xor(y)?)), 14 | } 15 | } 16 | } 17 | 18 | impl<'a, F: Field> BitXor for &'a Boolean { 19 | type Output = Boolean; 20 | 21 | /// Outputs `self ^ other`. 22 | /// 23 | /// If at least one of `self` and `other` are constants, then this method 24 | /// *does not* create any constraints or variables. 25 | /// 26 | /// ``` 27 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 28 | /// // We'll use the BLS12-381 scalar field for our constraints. 29 | /// use ark_test_curves::bls12_381::Fr; 30 | /// use ark_relations::r1cs::*; 31 | /// use ark_r1cs_std::prelude::*; 32 | /// 33 | /// let cs = ConstraintSystem::::new_ref(); 34 | /// 35 | /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; 36 | /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; 37 | /// 38 | /// (&a ^ &b).enforce_equal(&Boolean::TRUE)?; 39 | /// (&b ^ &a).enforce_equal(&Boolean::TRUE)?; 40 | /// 41 | /// (&a ^ &a).enforce_equal(&Boolean::FALSE)?; 42 | /// (&b ^ &b).enforce_equal(&Boolean::FALSE)?; 43 | /// 44 | /// assert!(cs.is_satisfied().unwrap()); 45 | /// # Ok(()) 46 | /// # } 47 | /// ``` 48 | #[tracing::instrument(target = "r1cs", skip(self, other))] 49 | fn bitxor(self, other: Self) -> Self::Output { 50 | self._xor(other).unwrap() 51 | } 52 | } 53 | 54 | impl<'a, F: Field> BitXor<&'a Self> for Boolean { 55 | type Output = Boolean; 56 | 57 | #[tracing::instrument(target = "r1cs", skip(self, other))] 58 | fn bitxor(self, other: &Self) -> Self::Output { 59 | self._xor(&other).unwrap() 60 | } 61 | } 62 | 63 | impl<'a, F: Field> BitXor> for &'a Boolean { 64 | type Output = Boolean; 65 | 66 | #[tracing::instrument(target = "r1cs", skip(self, other))] 67 | fn bitxor(self, other: Boolean) -> Self::Output { 68 | self._xor(&other).unwrap() 69 | } 70 | } 71 | 72 | impl BitXor for Boolean { 73 | type Output = Self; 74 | 75 | #[tracing::instrument(target = "r1cs", skip(self, other))] 76 | fn bitxor(self, other: Self) -> Self::Output { 77 | self._xor(&other).unwrap() 78 | } 79 | } 80 | 81 | impl BitXorAssign for Boolean { 82 | /// Sets `self = self ^ other`. 83 | #[tracing::instrument(target = "r1cs", skip(self, other))] 84 | fn bitxor_assign(&mut self, other: Self) { 85 | let result = self._xor(&other).unwrap(); 86 | *self = result; 87 | } 88 | } 89 | 90 | impl<'a, F: Field> BitXorAssign<&'a Self> for Boolean { 91 | /// Sets `self = self ^ other`. 92 | #[tracing::instrument(target = "r1cs", skip(self, other))] 93 | fn bitxor_assign(&mut self, other: &'a Self) { 94 | let result = self._xor(other).unwrap(); 95 | *self = result; 96 | } 97 | } 98 | 99 | #[cfg(test)] 100 | mod tests { 101 | use super::*; 102 | use crate::{ 103 | alloc::{AllocVar, AllocationMode}, 104 | boolean::test_utils::run_binary_exhaustive, 105 | prelude::EqGadget, 106 | R1CSVar, 107 | }; 108 | use ark_test_curves::bls12_381::Fr; 109 | 110 | #[test] 111 | fn xor() { 112 | run_binary_exhaustive::(|a, b| { 113 | let cs = a.cs().or(b.cs()); 114 | let both_constant = a.is_constant() && b.is_constant(); 115 | let computed = &a ^ &b; 116 | let expected_mode = if both_constant { 117 | AllocationMode::Constant 118 | } else { 119 | AllocationMode::Witness 120 | }; 121 | let expected = 122 | Boolean::new_variable(cs.clone(), || Ok(a.value()? ^ b.value()?), expected_mode)?; 123 | assert_eq!(expected.value(), computed.value()); 124 | expected.enforce_equal(&computed)?; 125 | if !both_constant { 126 | assert!(cs.is_satisfied().unwrap()); 127 | } 128 | Ok(()) 129 | }) 130 | .unwrap() 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/uint/prim_uint.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Shl, ShlAssign, Shr, ShrAssign}; 2 | use core::usize; 3 | 4 | #[doc(hidden)] 5 | // Adapted from 6 | pub trait PrimUInt: 7 | core::fmt::Debug 8 | + num_traits::PrimInt 9 | + num_traits::WrappingAdd 10 | + num_traits::SaturatingAdd 11 | + Shl 12 | + Shl 13 | + Shl 14 | + Shl 15 | + Shl 16 | + Shl 17 | + Shr 18 | + Shr 19 | + Shr 20 | + Shr 21 | + Shr 22 | + Shr 23 | + ShlAssign 24 | + ShlAssign 25 | + ShlAssign 26 | + ShlAssign 27 | + ShlAssign 28 | + ShlAssign 29 | + ShrAssign 30 | + ShrAssign 31 | + ShrAssign 32 | + ShrAssign 33 | + ShrAssign 34 | + ShrAssign 35 | + Into 36 | + _private::Sealed 37 | + ark_std::UniformRand 38 | { 39 | type Bytes: NumBytes; 40 | const MAX: Self; 41 | #[doc(hidden)] 42 | const MAX_VALUE_BIT_DECOMP: &'static [bool]; 43 | 44 | /// Return the memory representation of this number as a byte array in little-endian byte order. 45 | /// 46 | /// # Examples 47 | /// 48 | /// ``` 49 | /// use ark_r1cs_std::uint::PrimUInt; 50 | /// 51 | /// let bytes = PrimUInt::to_le_bytes(&0x12345678u32); 52 | /// assert_eq!(bytes, [0x78, 0x56, 0x34, 0x12]); 53 | /// ``` 54 | fn to_le_bytes(&self) -> Self::Bytes; 55 | 56 | /// Return the memory representation of this number as a byte array in big-endian byte order. 57 | /// 58 | /// # Examples 59 | /// 60 | /// ``` 61 | /// use ark_r1cs_std::uint::PrimUInt; 62 | /// 63 | /// let bytes = PrimUInt::to_be_bytes(&0x12345678u32); 64 | /// assert_eq!(bytes, [0x12, 0x34, 0x56, 0x78]); 65 | /// ``` 66 | fn to_be_bytes(&self) -> Self::Bytes; 67 | } 68 | 69 | impl PrimUInt for u8 { 70 | const MAX: Self = u8::MAX; 71 | const MAX_VALUE_BIT_DECOMP: &'static [bool] = &[true; 8]; 72 | type Bytes = [u8; 1]; 73 | 74 | #[inline] 75 | fn to_le_bytes(&self) -> Self::Bytes { 76 | u8::to_le_bytes(*self) 77 | } 78 | 79 | #[inline] 80 | fn to_be_bytes(&self) -> Self::Bytes { 81 | u8::to_be_bytes(*self) 82 | } 83 | } 84 | 85 | impl PrimUInt for u16 { 86 | const MAX: Self = u16::MAX; 87 | const MAX_VALUE_BIT_DECOMP: &'static [bool] = &[true; 16]; 88 | type Bytes = [u8; 2]; 89 | 90 | #[inline] 91 | fn to_le_bytes(&self) -> Self::Bytes { 92 | u16::to_le_bytes(*self) 93 | } 94 | 95 | #[inline] 96 | fn to_be_bytes(&self) -> Self::Bytes { 97 | u16::to_be_bytes(*self) 98 | } 99 | } 100 | 101 | impl PrimUInt for u32 { 102 | const MAX: Self = u32::MAX; 103 | const MAX_VALUE_BIT_DECOMP: &'static [bool] = &[true; 32]; 104 | type Bytes = [u8; 4]; 105 | 106 | #[inline] 107 | fn to_le_bytes(&self) -> Self::Bytes { 108 | u32::to_le_bytes(*self) 109 | } 110 | 111 | #[inline] 112 | fn to_be_bytes(&self) -> Self::Bytes { 113 | u32::to_be_bytes(*self) 114 | } 115 | } 116 | 117 | impl PrimUInt for u64 { 118 | const MAX: Self = u64::MAX; 119 | const MAX_VALUE_BIT_DECOMP: &'static [bool] = &[true; 64]; 120 | type Bytes = [u8; 8]; 121 | 122 | #[inline] 123 | fn to_le_bytes(&self) -> Self::Bytes { 124 | u64::to_le_bytes(*self) 125 | } 126 | 127 | #[inline] 128 | fn to_be_bytes(&self) -> Self::Bytes { 129 | u64::to_be_bytes(*self) 130 | } 131 | } 132 | 133 | impl PrimUInt for u128 { 134 | const MAX: Self = u128::MAX; 135 | const MAX_VALUE_BIT_DECOMP: &'static [bool] = &[true; 128]; 136 | type Bytes = [u8; 16]; 137 | 138 | #[inline] 139 | fn to_le_bytes(&self) -> Self::Bytes { 140 | u128::to_le_bytes(*self) 141 | } 142 | 143 | #[inline] 144 | fn to_be_bytes(&self) -> Self::Bytes { 145 | u128::to_be_bytes(*self) 146 | } 147 | } 148 | 149 | #[doc(hidden)] 150 | pub trait NumBytes: 151 | core::fmt::Debug 152 | + AsRef<[u8]> 153 | + AsMut<[u8]> 154 | + PartialEq 155 | + Eq 156 | + PartialOrd 157 | + Ord 158 | + core::hash::Hash 159 | + core::borrow::Borrow<[u8]> 160 | + core::borrow::BorrowMut<[u8]> 161 | { 162 | } 163 | 164 | #[doc(hidden)] 165 | impl NumBytes for [u8; N] {} 166 | 167 | mod _private { 168 | pub trait Sealed {} 169 | 170 | impl Sealed for u8 {} 171 | impl Sealed for u16 {} 172 | impl Sealed for u32 {} 173 | impl Sealed for u64 {} 174 | impl Sealed for u128 {} 175 | } 176 | -------------------------------------------------------------------------------- /src/boolean/select.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | impl Boolean { 4 | /// Conditionally selects one of `first` and `second` based on the value of 5 | /// `self`: 6 | /// 7 | /// If `self.is_eq(&Boolean::TRUE)`, this outputs `first`; else, it outputs 8 | /// `second`. 9 | /// ``` 10 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 11 | /// // We'll use the BLS12-381 scalar field for our constraints. 12 | /// use ark_test_curves::bls12_381::Fr; 13 | /// use ark_relations::r1cs::*; 14 | /// use ark_r1cs_std::prelude::*; 15 | /// 16 | /// let cs = ConstraintSystem::::new_ref(); 17 | /// 18 | /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; 19 | /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; 20 | /// 21 | /// let cond = Boolean::new_witness(cs.clone(), || Ok(true))?; 22 | /// 23 | /// cond.select(&a, &b)?.enforce_equal(&Boolean::TRUE)?; 24 | /// cond.select(&b, &a)?.enforce_equal(&Boolean::FALSE)?; 25 | /// 26 | /// assert!(cs.is_satisfied().unwrap()); 27 | /// # Ok(()) 28 | /// # } 29 | /// ``` 30 | #[tracing::instrument(target = "r1cs", skip(first, second))] 31 | pub fn select>( 32 | &self, 33 | first: &T, 34 | second: &T, 35 | ) -> Result { 36 | T::conditionally_select(&self, first, second) 37 | } 38 | } 39 | impl CondSelectGadget for Boolean { 40 | #[tracing::instrument(target = "r1cs")] 41 | fn conditionally_select( 42 | cond: &Boolean, 43 | true_val: &Self, 44 | false_val: &Self, 45 | ) -> Result { 46 | use Boolean::*; 47 | match cond { 48 | Constant(true) => Ok(true_val.clone()), 49 | Constant(false) => Ok(false_val.clone()), 50 | cond @ Var(_) => match (true_val, false_val) { 51 | (x, &Constant(false)) => Ok(cond & x), 52 | (&Constant(false), x) => Ok((!cond) & x), 53 | (&Constant(true), x) => Ok(cond | x), 54 | (x, &Constant(true)) => Ok((!cond) | x), 55 | (a, b) => { 56 | let cs = cond.cs(); 57 | let result: Boolean = 58 | AllocatedBool::new_witness_without_booleanity_check(cs.clone(), || { 59 | let cond = cond.value()?; 60 | Ok(if cond { a.value()? } else { b.value()? }) 61 | })? 62 | .into(); 63 | // a = self; b = other; c = cond; 64 | // 65 | // r = c * a + (1 - c) * b 66 | // r = b + c * (a - b) 67 | // c * (a - b) = r - b 68 | // 69 | // If a, b, cond are all boolean, so is r. 70 | // 71 | // self | other | cond | result 72 | // -----|-------|---------------- 73 | // 0 | 0 | 1 | 0 74 | // 0 | 1 | 1 | 0 75 | // 1 | 0 | 1 | 1 76 | // 1 | 1 | 1 | 1 77 | // 0 | 0 | 0 | 0 78 | // 0 | 1 | 0 | 1 79 | // 1 | 0 | 0 | 0 80 | // 1 | 1 | 0 | 1 81 | cs.enforce_constraint( 82 | cond.lc(), 83 | lc!() + a.lc() - b.lc(), 84 | lc!() + result.lc() - b.lc(), 85 | )?; 86 | 87 | Ok(result) 88 | }, 89 | }, 90 | } 91 | } 92 | } 93 | 94 | #[cfg(test)] 95 | mod tests { 96 | use super::*; 97 | use crate::{ 98 | alloc::{AllocVar, AllocationMode}, 99 | boolean::test_utils::run_binary_exhaustive, 100 | prelude::EqGadget, 101 | R1CSVar, 102 | }; 103 | use ark_test_curves::bls12_381::Fr; 104 | 105 | #[test] 106 | fn or() { 107 | run_binary_exhaustive::(|a, b| { 108 | let cs = a.cs().or(b.cs()); 109 | let both_constant = a.is_constant() && b.is_constant(); 110 | let expected_mode = if both_constant { 111 | AllocationMode::Constant 112 | } else { 113 | AllocationMode::Witness 114 | }; 115 | for cond in [true, false] { 116 | let expected = Boolean::new_variable( 117 | cs.clone(), 118 | || Ok(if cond { a.value()? } else { b.value()? }), 119 | expected_mode, 120 | )?; 121 | let cond = Boolean::new_variable(cs.clone(), || Ok(cond), expected_mode)?; 122 | let computed = cond.select(&a, &b)?; 123 | 124 | assert_eq!(expected.value(), computed.value()); 125 | expected.enforce_equal(&computed)?; 126 | if !both_constant { 127 | assert!(cs.is_satisfied().unwrap()); 128 | } 129 | } 130 | Ok(()) 131 | }) 132 | .unwrap() 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/uint/mod.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::{Field, PrimeField}; 2 | use core::{borrow::Borrow, convert::TryFrom, fmt::Debug}; 3 | 4 | use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; 5 | 6 | use crate::{boolean::Boolean, prelude::*, Assignment, Vec}; 7 | 8 | mod add; 9 | mod and; 10 | mod cmp; 11 | mod convert; 12 | mod eq; 13 | mod not; 14 | mod or; 15 | mod rotate; 16 | mod select; 17 | mod shl; 18 | mod shr; 19 | mod xor; 20 | 21 | #[doc(hidden)] 22 | pub mod prim_uint; 23 | pub use prim_uint::*; 24 | 25 | #[cfg(test)] 26 | pub(crate) mod test_utils; 27 | 28 | /// This struct represent an unsigned `N` bit integer as a sequence of `N` [`Boolean`]s. 29 | #[derive(Clone, Debug)] 30 | pub struct UInt { 31 | #[doc(hidden)] 32 | pub bits: [Boolean; N], 33 | #[doc(hidden)] 34 | pub value: Option, 35 | } 36 | 37 | impl R1CSVar for UInt { 38 | type Value = T; 39 | 40 | fn cs(&self) -> ConstraintSystemRef { 41 | self.bits.as_ref().cs() 42 | } 43 | 44 | fn value(&self) -> Result { 45 | let mut value = T::zero(); 46 | for (i, bit) in self.bits.iter().enumerate() { 47 | value = value + (T::from(bit.value()? as u8).unwrap() << i); 48 | } 49 | debug_assert_eq!(self.value, Some(value)); 50 | Ok(value) 51 | } 52 | } 53 | 54 | impl UInt { 55 | pub const MAX: Self = Self { 56 | bits: [Boolean::TRUE; N], 57 | value: Some(T::MAX), 58 | }; 59 | 60 | /// Construct a constant [`UInt`] from the native unsigned integer type. 61 | /// 62 | /// This *does not* create new variables or constraints. 63 | /// 64 | /// ``` 65 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 66 | /// // We'll use the BLS12-381 scalar field for our constraints. 67 | /// use ark_test_curves::bls12_381::Fr; 68 | /// use ark_relations::r1cs::*; 69 | /// use ark_r1cs_std::prelude::*; 70 | /// 71 | /// let cs = ConstraintSystem::::new_ref(); 72 | /// let var = UInt8::new_witness(cs.clone(), || Ok(2))?; 73 | /// 74 | /// let constant = UInt8::constant(2); 75 | /// var.enforce_equal(&constant)?; 76 | /// assert!(cs.is_satisfied().unwrap()); 77 | /// # Ok(()) 78 | /// # } 79 | /// ``` 80 | pub fn constant(value: T) -> Self { 81 | let mut bits = [Boolean::FALSE; N]; 82 | let mut bit_values = value; 83 | 84 | for i in 0..N { 85 | bits[i] = Boolean::constant((bit_values & T::one()) == T::one()); 86 | bit_values = bit_values >> 1u8; 87 | } 88 | 89 | Self { 90 | bits, 91 | value: Some(value), 92 | } 93 | } 94 | 95 | /// Construct a constant vector of [`UInt`] from a vector of the native type 96 | /// 97 | /// This *does not* create any new variables or constraints. 98 | /// ``` 99 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 100 | /// // We'll use the BLS12-381 scalar field for our constraints. 101 | /// use ark_test_curves::bls12_381::Fr; 102 | /// use ark_relations::r1cs::*; 103 | /// use ark_r1cs_std::prelude::*; 104 | /// 105 | /// let cs = ConstraintSystem::::new_ref(); 106 | /// let var = vec![UInt8::new_witness(cs.clone(), || Ok(2))?]; 107 | /// 108 | /// let constant = UInt8::constant_vec(&[2]); 109 | /// var.enforce_equal(&constant)?; 110 | /// assert!(cs.is_satisfied().unwrap()); 111 | /// # Ok(()) 112 | /// # } 113 | /// ``` 114 | pub fn constant_vec(values: &[T]) -> Vec { 115 | values.iter().map(|v| Self::constant(*v)).collect() 116 | } 117 | 118 | /// Allocates a slice of `uN`'s as private witnesses. 119 | pub fn new_witness_vec( 120 | cs: impl Into>, 121 | values: &[impl Into> + Copy], 122 | ) -> Result, SynthesisError> { 123 | let ns = cs.into(); 124 | let cs = ns.cs(); 125 | let mut output_vec = Vec::with_capacity(values.len()); 126 | for value in values { 127 | let byte: Option = Into::into(*value); 128 | output_vec.push(Self::new_witness(cs.clone(), || byte.get())?); 129 | } 130 | Ok(output_vec) 131 | } 132 | } 133 | 134 | impl AllocVar 135 | for UInt 136 | { 137 | fn new_variable>( 138 | cs: impl Into>, 139 | f: impl FnOnce() -> Result, 140 | mode: AllocationMode, 141 | ) -> Result { 142 | let ns = cs.into(); 143 | let cs = ns.cs(); 144 | let value = f().map(|f| *f.borrow()).ok(); 145 | 146 | let mut values = [None; N]; 147 | if let Some(val) = value { 148 | values 149 | .iter_mut() 150 | .enumerate() 151 | .for_each(|(i, v)| *v = Some(((val >> i) & T::one()) == T::one())); 152 | } 153 | 154 | let mut bits = [Boolean::FALSE; N]; 155 | for (b, v) in bits.iter_mut().zip(&values) { 156 | *b = Boolean::new_variable(cs.clone(), || v.get(), mode)?; 157 | } 158 | Ok(Self { bits, value }) 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/uint/shl.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use ark_relations::r1cs::SynthesisError; 3 | use ark_std::{ops::Shl, ops::ShlAssign}; 4 | 5 | use crate::boolean::Boolean; 6 | 7 | use super::{PrimUInt, UInt}; 8 | 9 | impl UInt { 10 | fn _shl_u128(&self, other: u128) -> Result { 11 | if other < N as u128 { 12 | let mut bits = [Boolean::FALSE; N]; 13 | for (a, b) in bits[other as usize..].iter_mut().zip(&self.bits) { 14 | *a = b.clone(); 15 | } 16 | 17 | let value = self.value.and_then(|a| Some(a << other)); 18 | Ok(Self { bits, value }) 19 | } else { 20 | panic!("attempt to shift left with overflow") 21 | } 22 | } 23 | } 24 | 25 | impl Shl for UInt { 26 | type Output = Self; 27 | 28 | /// Output `self << other`. 29 | /// 30 | /// If at least one of `self` and `other` are constants, then this method 31 | /// *does not* create any constraints or variables. 32 | /// 33 | /// ``` 34 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 35 | /// // We'll use the BLS12-381 scalar field for our constraints. 36 | /// use ark_test_curves::bls12_381::Fr; 37 | /// use ark_relations::r1cs::*; 38 | /// use ark_r1cs_std::prelude::*; 39 | /// 40 | /// let cs = ConstraintSystem::::new_ref(); 41 | /// let a = UInt8::new_witness(cs.clone(), || Ok(16))?; 42 | /// let b = 1u8; 43 | /// let c = UInt8::new_witness(cs.clone(), || Ok(16 << 1))?; 44 | /// 45 | /// (a << b).enforce_equal(&c)?; 46 | /// assert!(cs.is_satisfied().unwrap()); 47 | /// # Ok(()) 48 | /// # } 49 | /// ``` 50 | #[tracing::instrument(target = "r1cs", skip(self, other))] 51 | fn shl(self, other: T2) -> Self::Output { 52 | self._shl_u128(other.into()).unwrap() 53 | } 54 | } 55 | 56 | impl<'a, const N: usize, T: PrimUInt, F: PrimeField, T2: PrimUInt> Shl for &'a UInt { 57 | type Output = UInt; 58 | 59 | #[tracing::instrument(target = "r1cs", skip(self, other))] 60 | fn shl(self, other: T2) -> Self::Output { 61 | self._shl_u128(other.into()).unwrap() 62 | } 63 | } 64 | 65 | impl ShlAssign for UInt { 66 | /// Sets `self = self << other`. 67 | /// 68 | /// If at least one of `self` and `other` are constants, then this method 69 | /// *does not* create any constraints or variables. 70 | /// 71 | /// ``` 72 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 73 | /// // We'll use the BLS12-381 scalar field for our constraints. 74 | /// use ark_test_curves::bls12_381::Fr; 75 | /// use ark_relations::r1cs::*; 76 | /// use ark_r1cs_std::prelude::*; 77 | /// 78 | /// let cs = ConstraintSystem::::new_ref(); 79 | /// let mut a = UInt8::new_witness(cs.clone(), || Ok(16))?; 80 | /// let b = 1u8; 81 | /// let c = UInt8::new_witness(cs.clone(), || Ok(16 << 1))?; 82 | /// 83 | /// a <<= b; 84 | /// a.enforce_equal(&c)?; 85 | /// assert!(cs.is_satisfied().unwrap()); 86 | /// # Ok(()) 87 | /// # } 88 | /// ``` 89 | #[tracing::instrument(target = "r1cs", skip(self, other))] 90 | fn shl_assign(&mut self, other: T2) { 91 | let result = self._shl_u128(other.into()).unwrap(); 92 | *self = result; 93 | } 94 | } 95 | 96 | #[cfg(test)] 97 | mod tests { 98 | use super::*; 99 | use crate::{ 100 | alloc::{AllocVar, AllocationMode}, 101 | prelude::EqGadget, 102 | uint::test_utils::{run_binary_exhaustive_native_only, run_binary_random_native_only}, 103 | R1CSVar, 104 | }; 105 | use ark_ff::PrimeField; 106 | use ark_test_curves::bls12_381::Fr; 107 | 108 | fn uint_shl( 109 | a: UInt, 110 | b: T, 111 | ) -> Result<(), SynthesisError> { 112 | let cs = a.cs(); 113 | let b = b.into() % (N as u128); 114 | let computed = &a << b; 115 | let expected_mode = if a.is_constant() { 116 | AllocationMode::Constant 117 | } else { 118 | AllocationMode::Witness 119 | }; 120 | let expected = 121 | UInt::::new_variable(cs.clone(), || Ok(a.value()? << b), expected_mode)?; 122 | assert_eq!(expected.value(), computed.value()); 123 | expected.enforce_equal(&computed)?; 124 | if !a.is_constant() { 125 | assert!(cs.is_satisfied().unwrap()); 126 | } 127 | Ok(()) 128 | } 129 | 130 | #[test] 131 | fn u8_shl() { 132 | run_binary_exhaustive_native_only(uint_shl::).unwrap() 133 | } 134 | 135 | #[test] 136 | fn u16_shl() { 137 | run_binary_random_native_only::<1000, 16, _, _>(uint_shl::).unwrap() 138 | } 139 | 140 | #[test] 141 | fn u32_shl() { 142 | run_binary_random_native_only::<1000, 32, _, _>(uint_shl::).unwrap() 143 | } 144 | 145 | #[test] 146 | fn u64_shl() { 147 | run_binary_random_native_only::<1000, 64, _, _>(uint_shl::).unwrap() 148 | } 149 | 150 | #[test] 151 | fn u128_shl() { 152 | run_binary_random_native_only::<1000, 128, _, _>(uint_shl::).unwrap() 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/uint/shr.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use ark_relations::r1cs::SynthesisError; 3 | use ark_std::{ops::Shr, ops::ShrAssign}; 4 | 5 | use crate::boolean::Boolean; 6 | 7 | use super::{PrimUInt, UInt}; 8 | 9 | impl UInt { 10 | fn _shr_u128(&self, other: u128) -> Result { 11 | if other < N as u128 { 12 | let mut bits = [Boolean::FALSE; N]; 13 | for (a, b) in bits.iter_mut().zip(&self.bits[other as usize..]) { 14 | *a = b.clone(); 15 | } 16 | 17 | let value = self.value.and_then(|a| Some(a >> other)); 18 | Ok(Self { bits, value }) 19 | } else { 20 | panic!("attempt to shift right with overflow") 21 | } 22 | } 23 | } 24 | 25 | impl Shr for UInt { 26 | type Output = Self; 27 | 28 | /// Output `self >> other`. 29 | /// 30 | /// If at least one of `self` and `other` are constants, then this method 31 | /// *does not* create any constraints or variables. 32 | /// 33 | /// ``` 34 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 35 | /// // We'll use the BLS12-381 scalar field for our constraints. 36 | /// use ark_test_curves::bls12_381::Fr; 37 | /// use ark_relations::r1cs::*; 38 | /// use ark_r1cs_std::prelude::*; 39 | /// 40 | /// let cs = ConstraintSystem::::new_ref(); 41 | /// let a = UInt8::new_witness(cs.clone(), || Ok(16))?; 42 | /// let b = 1u8; 43 | /// let c = UInt8::new_witness(cs.clone(), || Ok(16 >> 1))?; 44 | /// 45 | /// (a >> b).enforce_equal(&c)?; 46 | /// assert!(cs.is_satisfied().unwrap()); 47 | /// # Ok(()) 48 | /// # } 49 | /// ``` 50 | #[tracing::instrument(target = "r1cs", skip(self, other))] 51 | fn shr(self, other: T2) -> Self::Output { 52 | self._shr_u128(other.into()).unwrap() 53 | } 54 | } 55 | 56 | impl<'a, const N: usize, T: PrimUInt, F: PrimeField, T2: PrimUInt> Shr for &'a UInt { 57 | type Output = UInt; 58 | 59 | #[tracing::instrument(target = "r1cs", skip(self, other))] 60 | fn shr(self, other: T2) -> Self::Output { 61 | self._shr_u128(other.into()).unwrap() 62 | } 63 | } 64 | 65 | impl ShrAssign for UInt { 66 | /// Sets `self = self >> other`. 67 | /// 68 | /// If at least one of `self` and `other` are constants, then this method 69 | /// *does not* create any constraints or variables. 70 | /// 71 | /// ``` 72 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 73 | /// // We'll use the BLS12-381 scalar field for our constraints. 74 | /// use ark_test_curves::bls12_381::Fr; 75 | /// use ark_relations::r1cs::*; 76 | /// use ark_r1cs_std::prelude::*; 77 | /// 78 | /// let cs = ConstraintSystem::::new_ref(); 79 | /// let mut a = UInt8::new_witness(cs.clone(), || Ok(16))?; 80 | /// let b = 1u8; 81 | /// let c = UInt8::new_witness(cs.clone(), || Ok(16 >> 1))?; 82 | /// 83 | /// a >>= b; 84 | /// a.enforce_equal(&c)?; 85 | /// assert!(cs.is_satisfied().unwrap()); 86 | /// # Ok(()) 87 | /// # } 88 | /// ``` 89 | #[tracing::instrument(target = "r1cs", skip(self, other))] 90 | fn shr_assign(&mut self, other: T2) { 91 | let result = self._shr_u128(other.into()).unwrap(); 92 | *self = result; 93 | } 94 | } 95 | 96 | #[cfg(test)] 97 | mod tests { 98 | use super::*; 99 | use crate::{ 100 | alloc::{AllocVar, AllocationMode}, 101 | prelude::EqGadget, 102 | uint::test_utils::{run_binary_exhaustive_native_only, run_binary_random_native_only}, 103 | R1CSVar, 104 | }; 105 | use ark_ff::PrimeField; 106 | use ark_test_curves::bls12_381::Fr; 107 | 108 | fn uint_shr( 109 | a: UInt, 110 | b: T, 111 | ) -> Result<(), SynthesisError> { 112 | let cs = a.cs(); 113 | let b = b.into() % (N as u128); 114 | let computed = &a >> b; 115 | let expected_mode = if a.is_constant() { 116 | AllocationMode::Constant 117 | } else { 118 | AllocationMode::Witness 119 | }; 120 | let expected = 121 | UInt::::new_variable(cs.clone(), || Ok(a.value()? >> b), expected_mode)?; 122 | assert_eq!(expected.value(), computed.value()); 123 | expected.enforce_equal(&computed)?; 124 | if !a.is_constant() { 125 | assert!(cs.is_satisfied().unwrap()); 126 | } 127 | Ok(()) 128 | } 129 | 130 | #[test] 131 | fn u8_shr() { 132 | run_binary_exhaustive_native_only(uint_shr::).unwrap() 133 | } 134 | 135 | #[test] 136 | fn u16_shr() { 137 | run_binary_random_native_only::<1000, 16, _, _>(uint_shr::).unwrap() 138 | } 139 | 140 | #[test] 141 | fn u32_shr() { 142 | run_binary_random_native_only::<1000, 32, _, _>(uint_shr::).unwrap() 143 | } 144 | 145 | #[test] 146 | fn u64_shr() { 147 | run_binary_random_native_only::<1000, 64, _, _>(uint_shr::).unwrap() 148 | } 149 | 150 | #[test] 151 | fn u128_shr() { 152 | run_binary_random_native_only::<1000, 128, _, _>(uint_shr::).unwrap() 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/alloc.rs: -------------------------------------------------------------------------------- 1 | use crate::Vec; 2 | use ark_ff::Field; 3 | use ark_relations::r1cs::{Namespace, SynthesisError}; 4 | use core::borrow::Borrow; 5 | 6 | /// Describes the mode that a variable should be allocated in within 7 | /// a `ConstraintSystem`. 8 | #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone)] 9 | pub enum AllocationMode { 10 | /// Indicate to the `ConstraintSystem` that the high-level variable should 11 | /// be allocated as a constant. That is, no `Variable`s should be 12 | /// generated. 13 | Constant = 0, 14 | 15 | /// Indicate to the `ConstraintSystem` that the high-level variable should 16 | /// be allocated as a public input to the `ConstraintSystem`. 17 | Input = 1, 18 | 19 | /// Indicate to the `ConstraintSystem` that the high-level variable should 20 | /// be allocated as a private witness to the `ConstraintSystem`. 21 | Witness = 2, 22 | } 23 | 24 | impl AllocationMode { 25 | /// Outputs the maximum according to the relation `Constant < Input < 26 | /// Witness`. 27 | pub fn max(&self, other: Self) -> Self { 28 | use AllocationMode::*; 29 | match (self, other) { 30 | (Constant, _) => other, 31 | (Input, Constant) => *self, 32 | (Input, _) => other, 33 | (Witness, _) => *self, 34 | } 35 | } 36 | } 37 | 38 | /// Specifies how variables of type `Self` should be allocated in a 39 | /// `ConstraintSystem`. 40 | pub trait AllocVar: Sized { 41 | /// Allocates a new variable of type `Self` in the `ConstraintSystem` `cs`. 42 | /// The mode of allocation is decided by `mode`. 43 | fn new_variable>( 44 | cs: impl Into>, 45 | f: impl FnOnce() -> Result, 46 | mode: AllocationMode, 47 | ) -> Result; 48 | 49 | /// Allocates a new constant of type `Self` in the `ConstraintSystem` `cs`. 50 | /// 51 | /// This should *not* allocate any new variables or constraints in `cs`. 52 | #[tracing::instrument(target = "r1cs", skip(cs, t))] 53 | fn new_constant( 54 | cs: impl Into>, 55 | t: impl Borrow, 56 | ) -> Result { 57 | Self::new_variable(cs, || Ok(t), AllocationMode::Constant) 58 | } 59 | 60 | /// Allocates a new public input of type `Self` in the `ConstraintSystem` 61 | /// `cs`. 62 | #[tracing::instrument(target = "r1cs", skip(cs, f))] 63 | fn new_input>( 64 | cs: impl Into>, 65 | f: impl FnOnce() -> Result, 66 | ) -> Result { 67 | Self::new_variable(cs, f, AllocationMode::Input) 68 | } 69 | 70 | /// Allocates a new private witness of type `Self` in the `ConstraintSystem` 71 | /// `cs`. 72 | #[tracing::instrument(target = "r1cs", skip(cs, f))] 73 | fn new_witness>( 74 | cs: impl Into>, 75 | f: impl FnOnce() -> Result, 76 | ) -> Result { 77 | Self::new_variable(cs, f, AllocationMode::Witness) 78 | } 79 | } 80 | 81 | /// This blanket implementation just allocates variables in `Self` 82 | /// element by element. 83 | impl> AllocVar<[I], F> for Vec { 84 | fn new_variable>( 85 | cs: impl Into>, 86 | f: impl FnOnce() -> Result, 87 | mode: AllocationMode, 88 | ) -> Result { 89 | let ns = cs.into(); 90 | let cs = ns.cs(); 91 | f().and_then(|v| { 92 | v.borrow() 93 | .iter() 94 | .map(|e| A::new_variable(cs.clone(), || Ok(e), mode)) 95 | .collect() 96 | }) 97 | } 98 | } 99 | 100 | /// Dummy impl for `()`. 101 | impl AllocVar<(), F> for () { 102 | fn new_variable>( 103 | _cs: impl Into>, 104 | _f: impl FnOnce() -> Result, 105 | _mode: AllocationMode, 106 | ) -> Result { 107 | Ok(()) 108 | } 109 | } 110 | 111 | /// This blanket implementation just allocates variables in `Self` 112 | /// element by element. 113 | impl, const N: usize> AllocVar<[I; N], F> for [A; N] { 114 | fn new_variable>( 115 | cs: impl Into>, 116 | f: impl FnOnce() -> Result, 117 | mode: AllocationMode, 118 | ) -> Result { 119 | let ns = cs.into(); 120 | let cs = ns.cs(); 121 | f().map(|v| { 122 | let v = v.borrow(); 123 | core::array::from_fn(|i| A::new_variable(cs.clone(), || Ok(&v[i]), mode).unwrap()) 124 | }) 125 | } 126 | } 127 | 128 | /// This blanket implementation just allocates variables in `Self` 129 | /// element by element. 130 | impl, const N: usize> AllocVar<[I], F> for [A; N] { 131 | fn new_variable>( 132 | cs: impl Into>, 133 | f: impl FnOnce() -> Result, 134 | mode: AllocationMode, 135 | ) -> Result { 136 | let ns = cs.into(); 137 | let cs = ns.cs(); 138 | f().map(|v| { 139 | let v = v.borrow(); 140 | core::array::from_fn(|i| A::new_variable(cs.clone(), || Ok(&v[i]), mode).unwrap()) 141 | }) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | env: 8 | RUST_BACKTRACE: 1 9 | 10 | jobs: 11 | style: 12 | name: Check Style 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | - name: Checkout 17 | uses: actions/checkout@v3 18 | 19 | - name: Install Rust 20 | uses: dtolnay/rust-toolchain@stable 21 | with: 22 | components: rustfmt 23 | 24 | - name: cargo fmt --check 25 | uses: actions-rs/cargo@v1 26 | with: 27 | command: fmt 28 | args: --all -- --check 29 | 30 | test: 31 | name: Test 32 | runs-on: ubuntu-latest 33 | env: 34 | RUSTFLAGS: -Dwarnings --cfg ci 35 | strategy: 36 | matrix: 37 | rust: 38 | - stable 39 | - nightly 40 | steps: 41 | - name: Checkout 42 | uses: actions/checkout@v3 43 | 44 | - name: Install Rust (${{ matrix.rust }}) 45 | uses: dtolnay/rust-toolchain@master 46 | with: 47 | toolchain: ${{ matrix.rust }} 48 | id: toolchain 49 | - run: rustup override set ${{steps.toolchain.outputs.name}} 50 | 51 | - uses: actions/cache@v2 52 | with: 53 | path: | 54 | ~/.cargo/registry 55 | ~/.cargo/git 56 | target 57 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 58 | 59 | - name: Check examples 60 | uses: actions-rs/cargo@v1 61 | with: 62 | command: check 63 | args: --examples --all 64 | 65 | - name: Check examples with all features on stable 66 | uses: actions-rs/cargo@v1 67 | with: 68 | command: check 69 | args: --examples --all-features --all 70 | if: matrix.rust == 'stable' 71 | 72 | - name: Check benchmarks on nightly 73 | uses: actions-rs/cargo@v1 74 | with: 75 | command: check 76 | args: --all-features --examples --all --benches 77 | if: matrix.rust == 'nightly' 78 | 79 | - name: Test 80 | uses: actions-rs/cargo@v1 81 | with: 82 | command: test 83 | args: "--all \ 84 | --all-features \ 85 | --exclude cp-benches " 86 | 87 | docs: 88 | name: Check Documentation 89 | runs-on: ubuntu-latest 90 | steps: 91 | 92 | - name: Checkout 93 | uses: actions/checkout@v3 94 | - name: Install Rust 95 | uses: dtolnay/rust-toolchain@stable 96 | with: 97 | components: rustfmt 98 | 99 | - name: cargo doc --all --no-deps --document-private-items --all-features 100 | uses: actions-rs/cargo@v1 101 | with: 102 | command: doc 103 | args: --all --no-deps --document-private-items --all-features 104 | 105 | check_no_std: 106 | name: Check no_std 107 | runs-on: ubuntu-latest 108 | steps: 109 | - name: Checkout 110 | uses: actions/checkout@v3 111 | 112 | - name: Install Rust 113 | uses: dtolnay/rust-toolchain@stable 114 | id: toolchain-thumbv6m 115 | with: 116 | target: thumbv6m-none-eabi 117 | - run: rustup override set ${{steps.toolchain-thumbv6m.outputs.name}} 118 | 119 | - name: Install Rust ARM64 120 | uses: dtolnay/rust-toolchain@stable 121 | id: toolchain-aarch64 122 | with: 123 | target: aarch64-unknown-none 124 | - run: rustup override set ${{steps.toolchain-aarch64.outputs.name}} 125 | 126 | - uses: actions/cache@v2 127 | with: 128 | path: | 129 | ~/.cargo/registry 130 | ~/.cargo/git 131 | target 132 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 133 | 134 | - name: r1cs-std 135 | run: | 136 | cargo build --no-default-features --target aarch64-unknown-none 137 | cargo check --examples --no-default-features --target aarch64-unknown-none 138 | 139 | test_against_curves: 140 | name: Test against curves 141 | runs-on: ubuntu-latest 142 | env: 143 | RUSTFLAGS: -Dwarnings 144 | strategy: 145 | matrix: 146 | curve: 147 | - bls12_377 148 | - pallas 149 | - vesta 150 | - mnt4_298 151 | - mnt6_298 152 | - ed_on_bls12_381 153 | steps: 154 | - name: Checkout curves 155 | uses: actions/checkout@v4 156 | with: 157 | repository: arkworks-rs/algebra 158 | 159 | - name: Checkout r1cs-std 160 | uses: actions/checkout@v4 161 | with: 162 | path: r1cs-std 163 | 164 | - name: Install Rust 165 | uses: dtolnay/rust-toolchain@stable 166 | 167 | - name: Patch cargo.toml 168 | run: | 169 | cd curves 170 | if grep -q "\[patch.crates-io\]" Cargo.toml ; then 171 | MATCH=$(awk '/\[patch.crates-io\]/{ print NR; exit }' Cargo.toml); 172 | sed -i "$MATCH,\$d" Cargo.toml 173 | fi 174 | { 175 | echo "[patch.crates-io]" 176 | echo "ark-std = { git = 'https://github.com/arkworks-rs/std' }" 177 | echo "ark-ec = { path = '../ec' }" 178 | echo "ark-ff = { path = '../ff' }" 179 | echo "ark-poly = { path = '../poly' }" 180 | echo "ark-relations = { git = 'https://github.com/arkworks-rs/snark' }" 181 | echo "ark-serialize = { path = '../serialize' }" 182 | echo "ark-algebra-bench-templates = { path = '../bench-templates' }" 183 | echo "ark-algebra-test-templates = { path = '../test-templates' }" 184 | echo "ark-r1cs-std = { path = '../r1cs-std' }" 185 | } >> Cargo.toml 186 | cd ${{ matrix.curve }} && cargo test --features 'r1cs' -------------------------------------------------------------------------------- /src/convert.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::Field; 2 | use ark_relations::r1cs::SynthesisError; 3 | use ark_std::vec::Vec; 4 | 5 | use crate::{boolean::Boolean, uint8::UInt8}; 6 | 7 | /// Specifies constraints for conversion to a little-endian bit representation 8 | /// of `self`. 9 | pub trait ToBitsGadget { 10 | /// Outputs the canonical little-endian bit-wise representation of `self`. 11 | /// 12 | /// This is the correct default for 99% of use cases. 13 | fn to_bits_le(&self) -> Result>, SynthesisError>; 14 | 15 | /// Outputs a possibly non-unique little-endian bit-wise representation of 16 | /// `self`. 17 | /// 18 | /// If you're not absolutely certain that your usecase can get away with a 19 | /// non-canonical representation, please use `self.to_bits()` instead. 20 | fn to_non_unique_bits_le(&self) -> Result>, SynthesisError> { 21 | self.to_bits_le() 22 | } 23 | 24 | /// Outputs the canonical big-endian bit-wise representation of `self`. 25 | fn to_bits_be(&self) -> Result>, SynthesisError> { 26 | let mut res = self.to_bits_le()?; 27 | res.reverse(); 28 | Ok(res) 29 | } 30 | 31 | /// Outputs a possibly non-unique big-endian bit-wise representation of 32 | /// `self`. 33 | fn to_non_unique_bits_be(&self) -> Result>, SynthesisError> { 34 | let mut res = self.to_non_unique_bits_le()?; 35 | res.reverse(); 36 | Ok(res) 37 | } 38 | } 39 | 40 | impl ToBitsGadget for Boolean { 41 | fn to_bits_le(&self) -> Result>, SynthesisError> { 42 | Ok(vec![self.clone()]) 43 | } 44 | } 45 | 46 | impl ToBitsGadget for [Boolean] { 47 | /// Outputs `self`. 48 | fn to_bits_le(&self) -> Result>, SynthesisError> { 49 | Ok(self.to_vec()) 50 | } 51 | } 52 | 53 | impl ToBitsGadget for Vec 54 | where 55 | [T]: ToBitsGadget, 56 | { 57 | fn to_bits_le(&self) -> Result>, SynthesisError> { 58 | self.as_slice().to_bits_le().map(|v| v.to_vec()) 59 | } 60 | 61 | fn to_non_unique_bits_le(&self) -> Result>, SynthesisError> { 62 | self.as_slice().to_non_unique_bits_le().map(|v| v.to_vec()) 63 | } 64 | } 65 | 66 | /// Specifies constraints for conversion to a little-endian byte representation 67 | /// of `self`. 68 | pub trait ToBytesGadget { 69 | /// Outputs a canonical, little-endian, byte decomposition of `self`. 70 | /// 71 | /// This is the correct default for 99% of use cases. 72 | fn to_bytes_le(&self) -> Result>, SynthesisError>; 73 | 74 | /// Outputs a possibly non-unique byte decomposition of `self`. 75 | /// 76 | /// If you're not absolutely certain that your usecase can get away with a 77 | /// non-canonical representation, please use `self.to_bytes_le(cs)` instead. 78 | fn to_non_unique_bytes_le(&self) -> Result>, SynthesisError> { 79 | self.to_bytes_le() 80 | } 81 | } 82 | 83 | impl<'a, F: Field, T: 'a + ToBytesGadget> ToBytesGadget for &'a T { 84 | fn to_bytes_le(&self) -> Result>, SynthesisError> { 85 | (*self).to_bytes_le() 86 | } 87 | 88 | fn to_non_unique_bytes_le(&self) -> Result>, SynthesisError> { 89 | (*self).to_non_unique_bytes_le() 90 | } 91 | } 92 | 93 | impl, F: Field> ToBytesGadget for [T] { 94 | fn to_bytes_le(&self) -> Result>, SynthesisError> { 95 | let mut bytes = Vec::new(); 96 | for elem in self { 97 | let elem = elem.to_bytes_le()?; 98 | bytes.extend_from_slice(&elem); 99 | // Make sure that there's enough capacity to avoid reallocations. 100 | bytes.reserve(elem.len() * (self.len() - 1)); 101 | } 102 | Ok(bytes) 103 | } 104 | 105 | fn to_non_unique_bytes_le(&self) -> Result>, SynthesisError> { 106 | let mut bytes = Vec::new(); 107 | for elem in self { 108 | let elem = elem.to_non_unique_bytes_le()?; 109 | bytes.extend_from_slice(&elem); 110 | // Make sure that there's enough capacity to avoid reallocations. 111 | bytes.reserve(elem.len() * (self.len() - 1)); 112 | } 113 | Ok(bytes) 114 | } 115 | } 116 | 117 | impl, F: Field> ToBytesGadget for Vec { 118 | fn to_bytes_le(&self) -> Result>, SynthesisError> { 119 | self.as_slice().to_bytes_le() 120 | } 121 | 122 | fn to_non_unique_bytes_le(&self) -> Result>, SynthesisError> { 123 | self.as_slice().to_non_unique_bytes_le() 124 | } 125 | } 126 | 127 | impl, F: Field, const N: usize> ToBytesGadget for [T; N] { 128 | fn to_bytes_le(&self) -> Result>, SynthesisError> { 129 | self.as_slice().to_bytes_le() 130 | } 131 | 132 | fn to_non_unique_bytes_le(&self) -> Result>, SynthesisError> { 133 | self.as_slice().to_non_unique_bytes_le() 134 | } 135 | } 136 | 137 | impl ToBytesGadget for () { 138 | fn to_bytes_le(&self) -> Result>, SynthesisError> { 139 | Ok(Vec::new()) 140 | } 141 | 142 | fn to_non_unique_bytes_le(&self) -> Result>, SynthesisError> { 143 | Ok(Vec::new()) 144 | } 145 | } 146 | 147 | /// Specifies how to convert a variable of type `Self` to variables of 148 | /// type `FpVar` 149 | pub trait ToConstraintFieldGadget { 150 | /// Converts `self` to `FpVar` variables. 151 | fn to_constraint_field( 152 | &self, 153 | ) -> Result>, ark_relations::r1cs::SynthesisError>; 154 | } 155 | -------------------------------------------------------------------------------- /src/uint/eq.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use ark_relations::r1cs::SynthesisError; 3 | use ark_std::vec::Vec; 4 | 5 | use crate::boolean::Boolean; 6 | use crate::eq::EqGadget; 7 | 8 | use super::*; 9 | 10 | impl EqGadget 11 | for UInt 12 | { 13 | #[tracing::instrument(target = "r1cs", skip(self, other))] 14 | fn is_eq(&self, other: &Self) -> Result, SynthesisError> { 15 | let chunk_size = usize::try_from(ConstraintF::MODULUS_BIT_SIZE - 1).unwrap(); 16 | let chunks_are_eq = self 17 | .bits 18 | .chunks(chunk_size) 19 | .zip(other.bits.chunks(chunk_size)) 20 | .map(|(a, b)| { 21 | let a = Boolean::le_bits_to_fp(a)?; 22 | let b = Boolean::le_bits_to_fp(b)?; 23 | a.is_eq(&b) 24 | }) 25 | .collect::, _>>()?; 26 | Boolean::kary_and(&chunks_are_eq) 27 | } 28 | 29 | #[tracing::instrument(target = "r1cs", skip(self, other))] 30 | fn conditional_enforce_equal( 31 | &self, 32 | other: &Self, 33 | condition: &Boolean, 34 | ) -> Result<(), SynthesisError> { 35 | let chunk_size = usize::try_from(ConstraintF::MODULUS_BIT_SIZE - 1).unwrap(); 36 | for (a, b) in self 37 | .bits 38 | .chunks(chunk_size) 39 | .zip(other.bits.chunks(chunk_size)) 40 | { 41 | let a = Boolean::le_bits_to_fp(a)?; 42 | let b = Boolean::le_bits_to_fp(b)?; 43 | a.conditional_enforce_equal(&b, condition)?; 44 | } 45 | Ok(()) 46 | } 47 | 48 | #[tracing::instrument(target = "r1cs", skip(self, other))] 49 | fn conditional_enforce_not_equal( 50 | &self, 51 | other: &Self, 52 | condition: &Boolean, 53 | ) -> Result<(), SynthesisError> { 54 | let chunk_size = usize::try_from(ConstraintF::MODULUS_BIT_SIZE - 1).unwrap(); 55 | for (a, b) in self 56 | .bits 57 | .chunks(chunk_size) 58 | .zip(other.bits.chunks(chunk_size)) 59 | { 60 | let a = Boolean::le_bits_to_fp(a)?; 61 | let b = Boolean::le_bits_to_fp(b)?; 62 | a.conditional_enforce_not_equal(&b, condition)?; 63 | } 64 | Ok(()) 65 | } 66 | } 67 | 68 | #[cfg(test)] 69 | mod tests { 70 | use super::*; 71 | use crate::{ 72 | alloc::{AllocVar, AllocationMode}, 73 | prelude::EqGadget, 74 | uint::test_utils::{run_binary_exhaustive, run_binary_random}, 75 | R1CSVar, 76 | }; 77 | use ark_ff::PrimeField; 78 | use ark_test_curves::bls12_381::Fr; 79 | 80 | fn uint_eq( 81 | a: UInt, 82 | b: UInt, 83 | ) -> Result<(), SynthesisError> { 84 | let cs = a.cs().or(b.cs()); 85 | let both_constant = a.is_constant() && b.is_constant(); 86 | let computed = a.is_eq(&b)?; 87 | let expected_mode = if both_constant { 88 | AllocationMode::Constant 89 | } else { 90 | AllocationMode::Witness 91 | }; 92 | let expected = 93 | Boolean::new_variable(cs.clone(), || Ok(a.value()? == b.value()?), expected_mode)?; 94 | assert_eq!(expected.value(), computed.value()); 95 | expected.enforce_equal(&computed)?; 96 | if !both_constant { 97 | assert!(cs.is_satisfied().unwrap()); 98 | } 99 | Ok(()) 100 | } 101 | 102 | fn uint_neq( 103 | a: UInt, 104 | b: UInt, 105 | ) -> Result<(), SynthesisError> { 106 | let cs = a.cs().or(b.cs()); 107 | let both_constant = a.is_constant() && b.is_constant(); 108 | let computed = a.is_neq(&b)?; 109 | let expected_mode = if both_constant { 110 | AllocationMode::Constant 111 | } else { 112 | AllocationMode::Witness 113 | }; 114 | let expected = 115 | Boolean::new_variable(cs.clone(), || Ok(a.value()? != b.value()?), expected_mode)?; 116 | assert_eq!(expected.value(), computed.value()); 117 | expected.enforce_equal(&computed)?; 118 | if !both_constant { 119 | assert!(cs.is_satisfied().unwrap()); 120 | } 121 | Ok(()) 122 | } 123 | 124 | #[test] 125 | fn u8_eq() { 126 | run_binary_exhaustive(uint_eq::).unwrap() 127 | } 128 | 129 | #[test] 130 | fn u16_eq() { 131 | run_binary_random::<1000, 16, _, _>(uint_eq::).unwrap() 132 | } 133 | 134 | #[test] 135 | fn u32_eq() { 136 | run_binary_random::<1000, 32, _, _>(uint_eq::).unwrap() 137 | } 138 | 139 | #[test] 140 | fn u64_eq() { 141 | run_binary_random::<1000, 64, _, _>(uint_eq::).unwrap() 142 | } 143 | 144 | #[test] 145 | fn u128_eq() { 146 | run_binary_random::<1000, 128, _, _>(uint_eq::).unwrap() 147 | } 148 | 149 | #[test] 150 | fn u8_neq() { 151 | run_binary_exhaustive(uint_neq::).unwrap() 152 | } 153 | 154 | #[test] 155 | fn u16_neq() { 156 | run_binary_random::<1000, 16, _, _>(uint_neq::).unwrap() 157 | } 158 | 159 | #[test] 160 | fn u32_neq() { 161 | run_binary_random::<1000, 32, _, _>(uint_neq::).unwrap() 162 | } 163 | 164 | #[test] 165 | fn u64_neq() { 166 | run_binary_random::<1000, 64, _, _>(uint_neq::).unwrap() 167 | } 168 | 169 | #[test] 170 | fn u128_neq() { 171 | run_binary_random::<1000, 128, _, _>(uint_neq::).unwrap() 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/pairing/bls12/mod.rs: -------------------------------------------------------------------------------- 1 | use ark_relations::r1cs::SynthesisError; 2 | 3 | use super::PairingVar as PG; 4 | 5 | use crate::{ 6 | fields::{fp::FpVar, fp12::Fp12Var, fp2::Fp2Var, FieldVar}, 7 | groups::bls12::{G1AffineVar, G1PreparedVar, G1Var, G2PreparedVar, G2Var}, 8 | }; 9 | use ark_ec::bls12::{Bls12, Bls12Config, TwistType}; 10 | use ark_ff::BitIteratorBE; 11 | use ark_std::marker::PhantomData; 12 | 13 | /// Specifies the constraints for computing a pairing in a BLS12 bilinear group. 14 | pub struct PairingVar(PhantomData

); 15 | 16 | type Fp2V

= Fp2Var<

::Fp2Config>; 17 | 18 | impl PairingVar

{ 19 | // Evaluate the line function at point p. 20 | #[tracing::instrument(target = "r1cs")] 21 | fn ell( 22 | f: &mut Fp12Var, 23 | coeffs: &(Fp2V

, Fp2V

), 24 | p: &G1AffineVar

, 25 | ) -> Result<(), SynthesisError> { 26 | let zero = FpVar::::zero(); 27 | 28 | match P::TWIST_TYPE { 29 | TwistType::M => { 30 | let c0 = coeffs.0.clone(); 31 | let mut c1 = coeffs.1.clone(); 32 | let c2 = Fp2V::

::new(p.y.clone(), zero); 33 | 34 | c1.c0 *= &p.x; 35 | c1.c1 *= &p.x; 36 | *f = f.mul_by_014(&c0, &c1, &c2)?; 37 | Ok(()) 38 | }, 39 | TwistType::D => { 40 | let c0 = Fp2V::

::new(p.y.clone(), zero); 41 | let mut c1 = coeffs.0.clone(); 42 | let c2 = coeffs.1.clone(); 43 | 44 | c1.c0 *= &p.x; 45 | c1.c1 *= &p.x; 46 | *f = f.mul_by_034(&c0, &c1, &c2)?; 47 | Ok(()) 48 | }, 49 | } 50 | } 51 | 52 | #[tracing::instrument(target = "r1cs")] 53 | fn exp_by_x(f: &Fp12Var) -> Result, SynthesisError> { 54 | let mut result = f.optimized_cyclotomic_exp(P::X)?; 55 | if P::X_IS_NEGATIVE { 56 | result = result.unitary_inverse()?; 57 | } 58 | Ok(result) 59 | } 60 | } 61 | 62 | impl PG> for PairingVar

{ 63 | type G1Var = G1Var

; 64 | type G2Var = G2Var

; 65 | type G1PreparedVar = G1PreparedVar

; 66 | type G2PreparedVar = G2PreparedVar

; 67 | type GTVar = Fp12Var; 68 | 69 | #[tracing::instrument(target = "r1cs")] 70 | fn miller_loop( 71 | ps: &[Self::G1PreparedVar], 72 | qs: &[Self::G2PreparedVar], 73 | ) -> Result { 74 | let mut pairs = vec![]; 75 | for (p, q) in ps.iter().zip(qs.iter()) { 76 | pairs.push((p, q.ell_coeffs.iter())); 77 | } 78 | let mut f = Self::GTVar::one(); 79 | 80 | for i in BitIteratorBE::new(P::X).skip(1) { 81 | f.square_in_place()?; 82 | 83 | for &mut (p, ref mut coeffs) in pairs.iter_mut() { 84 | Self::ell(&mut f, coeffs.next().unwrap(), &p.0)?; 85 | } 86 | 87 | if i { 88 | for &mut (p, ref mut coeffs) in pairs.iter_mut() { 89 | Self::ell(&mut f, &coeffs.next().unwrap(), &p.0)?; 90 | } 91 | } 92 | } 93 | 94 | if P::X_IS_NEGATIVE { 95 | f = f.unitary_inverse()?; 96 | } 97 | 98 | Ok(f) 99 | } 100 | 101 | #[tracing::instrument(target = "r1cs")] 102 | fn final_exponentiation(f: &Self::GTVar) -> Result { 103 | // Computing the final exponentation following 104 | // https://eprint.iacr.org/2016/130.pdf. 105 | // We don't use their "faster" formula because it is difficult to make 106 | // it work for curves with odd `P::X`. 107 | // Hence we implement the slower algorithm from Table 1 below. 108 | 109 | let f1 = f.unitary_inverse()?; 110 | 111 | f.inverse().and_then(|mut f2| { 112 | // f2 = f^(-1); 113 | // r = f^(p^6 - 1) 114 | let mut r = f1; 115 | r *= &f2; 116 | 117 | // f2 = f^(p^6 - 1) 118 | f2 = r.clone(); 119 | // r = f^((p^6 - 1)(p^2)) 120 | r.frobenius_map_in_place(2)?; 121 | 122 | // r = f^((p^6 - 1)(p^2) + (p^6 - 1)) 123 | // r = f^((p^6 - 1)(p^2 + 1)) 124 | r *= &f2; 125 | 126 | // Hard part of the final exponentation is below: 127 | // From https://eprint.iacr.org/2016/130.pdf, Table 1 128 | let mut y0 = r.cyclotomic_square()?; 129 | y0 = y0.unitary_inverse()?; 130 | 131 | let mut y5 = Self::exp_by_x(&r)?; 132 | 133 | let mut y1 = y5.cyclotomic_square()?; 134 | let mut y3 = y0 * &y5; 135 | y0 = Self::exp_by_x(&y3)?; 136 | let y2 = Self::exp_by_x(&y0)?; 137 | let mut y4 = Self::exp_by_x(&y2)?; 138 | y4 *= &y1; 139 | y1 = Self::exp_by_x(&y4)?; 140 | y3 = y3.unitary_inverse()?; 141 | y1 *= &y3; 142 | y1 *= &r; 143 | y3 = r.clone(); 144 | y3 = y3.unitary_inverse()?; 145 | y0 *= &r; 146 | y0.frobenius_map_in_place(3)?; 147 | y4 *= &y3; 148 | y4.frobenius_map_in_place(1)?; 149 | y5 *= &y2; 150 | y5.frobenius_map_in_place(2)?; 151 | y5 *= &y0; 152 | y5 *= &y4; 153 | y5 *= &y1; 154 | Ok(y5) 155 | }) 156 | } 157 | 158 | #[tracing::instrument(target = "r1cs")] 159 | fn prepare_g1(p: &Self::G1Var) -> Result { 160 | Self::G1PreparedVar::from_group_var(p) 161 | } 162 | 163 | #[tracing::instrument(target = "r1cs")] 164 | fn prepare_g2(q: &Self::G2Var) -> Result { 165 | Self::G2PreparedVar::from_group_var(q) 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/poly/evaluations/univariate/lagrange_interpolator.rs: -------------------------------------------------------------------------------- 1 | use crate::poly::domain::vanishing_poly::VanishingPolynomial; 2 | use ark_ff::{batch_inversion_and_mul, PrimeField}; 3 | use ark_std::vec::Vec; 4 | /// Struct describing Lagrange interpolation for a multiplicative coset I, 5 | /// with |I| a power of 2. 6 | /// TODO: Pull in lagrange poly explanation from libiop 7 | #[derive(Clone)] 8 | pub struct LagrangeInterpolator { 9 | pub(crate) domain_order: usize, 10 | pub(crate) all_domain_elems: Vec, 11 | pub(crate) v_inv_elems: Vec, 12 | pub(crate) domain_vp: VanishingPolynomial, 13 | poly_evaluations: Vec, 14 | } 15 | 16 | impl LagrangeInterpolator { 17 | /// Returns a lagrange interpolator, given the domain specification. 18 | pub fn new( 19 | domain_offset: F, 20 | domain_generator: F, 21 | domain_dim: u64, 22 | poly_evaluations: Vec, 23 | ) -> Self { 24 | let domain_order = 1 << domain_dim; 25 | assert_eq!(poly_evaluations.len(), domain_order); 26 | let mut cur_elem = domain_offset; 27 | let mut all_domain_elems = vec![domain_offset]; 28 | let mut v_inv_elems: Vec = Vec::new(); 29 | // Cache all elements in the domain 30 | for _ in 1..domain_order { 31 | cur_elem *= domain_generator; 32 | all_domain_elems.push(cur_elem); 33 | } 34 | // By computing the following elements as constants, 35 | // we can further reduce the interpolation costs. 36 | // 37 | // m = order of the interpolation domain 38 | // v_inv[i] = prod_{j != i} h(g^i - g^j) 39 | // We use the following facts to compute this: 40 | // v_inv[0] = m*h^{m-1} 41 | // v_inv[i] = g^{-1} * v_inv[i-1] 42 | // TODO: Include proof of the above two points 43 | let g_inv = domain_generator.inverse().unwrap(); 44 | let m = F::from((1 << domain_dim) as u128); 45 | let mut v_inv_i = m * domain_offset.pow([(domain_order - 1) as u64]); 46 | for _ in 0..domain_order { 47 | v_inv_elems.push(v_inv_i); 48 | v_inv_i *= g_inv; 49 | } 50 | 51 | // TODO: Cache the intermediate terms with Z_H(x) evaluations. 52 | let vp = VanishingPolynomial::new(domain_offset, domain_dim); 53 | 54 | let lagrange_interpolation: LagrangeInterpolator = LagrangeInterpolator { 55 | domain_order, 56 | all_domain_elems, 57 | v_inv_elems, 58 | domain_vp: vp, 59 | poly_evaluations, 60 | }; 61 | lagrange_interpolation 62 | } 63 | 64 | pub(crate) fn compute_lagrange_coefficients(&self, interpolation_point: F) -> Vec { 65 | // Let t be the interpolation point, H be the multiplicative coset, with 66 | // elements of the form h*g^i. Compute each L_{i,H}(t) as Z_{H}(t) * v_i 67 | // / (t- h g^i) where: 68 | // - Z_{H}(t) = \prod_{j} (t-h*g^j) = (t^m-h^m), and 69 | // - v_{i} = 1 / \prod_{j \neq i} h(g^i-g^j). 70 | // Below we use the fact that v_{0} = 1/(m * h^(m-1)) and v_{i+1} = g * v_{i}. 71 | // We first compute the inverse of each coefficient, except for the Z_H(t) term. 72 | // We then batch invert the entire result, and multiply by Z_H(t). 73 | let mut inverted_lagrange_coeffs: Vec = Vec::with_capacity(self.all_domain_elems.len()); 74 | for i in 0..self.domain_order { 75 | let l = self.v_inv_elems[i]; 76 | let r = self.all_domain_elems[i]; 77 | inverted_lagrange_coeffs.push(l * (interpolation_point - r)); 78 | } 79 | let vp_t = self.domain_vp.evaluate(&interpolation_point); 80 | let lagrange_coeffs = inverted_lagrange_coeffs.as_mut_slice(); 81 | batch_inversion_and_mul::(lagrange_coeffs, &vp_t); 82 | lagrange_coeffs.iter().cloned().collect() 83 | } 84 | 85 | pub fn interpolate(&self, interpolation_point: F) -> F { 86 | let lagrange_coeffs = self.compute_lagrange_coefficients(interpolation_point); 87 | let mut interpolation = F::zero(); 88 | for i in 0..self.domain_order { 89 | interpolation += lagrange_coeffs[i] * self.poly_evaluations[i]; 90 | } 91 | interpolation 92 | } 93 | } 94 | 95 | #[cfg(test)] 96 | mod tests { 97 | use crate::{ 98 | fields::{fp::FpVar, FieldVar}, 99 | poly::{ 100 | domain::Radix2DomainVar, 101 | evaluations::univariate::lagrange_interpolator::LagrangeInterpolator, 102 | }, 103 | R1CSVar, 104 | }; 105 | use ark_ff::{FftField, Field, One}; 106 | use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial}; 107 | use ark_std::{test_rng, UniformRand}; 108 | use ark_test_curves::bls12_381::Fr; 109 | 110 | #[test] 111 | pub fn test_native_interpolate() { 112 | let mut rng = test_rng(); 113 | let poly = DensePolynomial::rand(15, &mut rng); 114 | let gen = Fr::get_root_of_unity(1 << 4).unwrap(); 115 | assert_eq!(gen.pow(&[1 << 4]), Fr::one()); 116 | let domain = Radix2DomainVar::new( 117 | gen, 118 | 4, // 2^4 = 16 119 | FpVar::constant(Fr::GENERATOR), 120 | ) 121 | .unwrap(); 122 | // generate evaluations of `poly` on this domain 123 | let mut coset_point = domain.offset().value().unwrap(); 124 | let mut oracle_evals = Vec::new(); 125 | for _ in 0..(1 << 4) { 126 | oracle_evals.push(poly.evaluate(&coset_point)); 127 | coset_point *= gen; 128 | } 129 | 130 | let interpolator = LagrangeInterpolator::new( 131 | domain.offset().value().unwrap(), 132 | domain.gen, 133 | domain.dim, 134 | oracle_evals, 135 | ); 136 | 137 | // the point to evaluate at 138 | let interpolate_point = Fr::rand(&mut rng); 139 | 140 | let expected = poly.evaluate(&interpolate_point); 141 | let actual = interpolator.interpolate(interpolate_point); 142 | 143 | assert_eq!(actual, expected) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/groups/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | convert::{ToBitsGadget, ToBytesGadget}, 3 | fields::emulated_fp::EmulatedFpVar, 4 | prelude::*, 5 | }; 6 | use ark_ff::PrimeField; 7 | use ark_relations::r1cs::{Namespace, SynthesisError}; 8 | use core::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; 9 | 10 | use ark_ec::CurveGroup; 11 | use core::{borrow::Borrow, fmt::Debug}; 12 | 13 | /// This module contains implementations of arithmetic for various curve models. 14 | pub mod curves; 15 | 16 | pub use self::curves::short_weierstrass::{bls12, mnt4, mnt6}; 17 | 18 | /// A hack used to work around the lack of implied bounds. 19 | pub trait GroupOpsBounds<'a, G, T: 'a>: 20 | Sized 21 | + Add<&'a T, Output = T> 22 | + Sub<&'a T, Output = T> 23 | + Add 24 | + Sub 25 | + Add 26 | + Sub 27 | { 28 | } 29 | 30 | /// A variable that represents a curve point for 31 | /// the curve `C`. 32 | pub trait CurveVar: 33 | 'static 34 | + Sized 35 | + Clone 36 | + Debug 37 | + R1CSVar 38 | + ToBitsGadget 39 | + ToBytesGadget 40 | + EqGadget 41 | + CondSelectGadget 42 | + AllocVar 43 | + AllocVar 44 | + for<'a> GroupOpsBounds<'a, C, Self> 45 | + for<'a> AddAssign<&'a Self> 46 | + for<'a> SubAssign<&'a Self> 47 | + AddAssign 48 | + SubAssign 49 | + AddAssign 50 | + SubAssign 51 | + Mul, Output = Self> 52 | + for<'a> Mul<&'a EmulatedFpVar, Output = Self> 53 | + MulAssign> 54 | { 55 | /// Returns the constant `F::zero()`. This is the identity 56 | /// of the group. 57 | fn zero() -> Self; 58 | 59 | /// Returns a `Boolean` representing whether `self == Self::zero()`. 60 | #[tracing::instrument(target = "r1cs")] 61 | fn is_zero(&self) -> Result, SynthesisError> { 62 | self.is_eq(&Self::zero()) 63 | } 64 | 65 | /// Returns a constant with value `v`. 66 | /// 67 | /// This *should not* allocate any variables. 68 | fn constant(other: C) -> Self; 69 | 70 | /// Allocates a variable in the subgroup without checking if it's in the 71 | /// prime-order subgroup. 72 | fn new_variable_omit_prime_order_check( 73 | cs: impl Into>, 74 | f: impl FnOnce() -> Result, 75 | mode: AllocationMode, 76 | ) -> Result; 77 | 78 | /// Enforce that `self` is in the prime-order subgroup. 79 | fn enforce_prime_order(&self) -> Result<(), SynthesisError>; 80 | 81 | /// Computes `self + self`. 82 | #[tracing::instrument(target = "r1cs")] 83 | fn double(&self) -> Result { 84 | let mut result = self.clone(); 85 | result.double_in_place()?; 86 | Ok(result) 87 | } 88 | 89 | /// Sets `self = self + self`. 90 | fn double_in_place(&mut self) -> Result<(), SynthesisError>; 91 | 92 | /// Coputes `-self`. 93 | fn negate(&self) -> Result; 94 | 95 | /// Computes `bits * self`, where `bits` is a little-endian 96 | /// `Boolean` representation of a scalar. 97 | #[tracing::instrument(target = "r1cs", skip(bits))] 98 | fn scalar_mul_le<'a>( 99 | &self, 100 | bits: impl Iterator>, 101 | ) -> Result { 102 | // TODO: in the constant case we should call precomputed_scalar_mul_le, 103 | // but rn there's a bug when doing this with TE curves. 104 | 105 | // Computes the standard little-endian double-and-add algorithm 106 | // (Algorithm 3.26, Guide to Elliptic Curve Cryptography) 107 | let mut res = Self::zero(); 108 | let mut multiple = self.clone(); 109 | for bit in bits { 110 | let tmp = res.clone() + &multiple; 111 | res = bit.select(&tmp, &res)?; 112 | multiple.double_in_place()?; 113 | } 114 | Ok(res) 115 | } 116 | 117 | /// Computes a `I * self` in place, where `I` is a `Boolean` *little-endian* 118 | /// representation of the scalar. 119 | /// 120 | /// The bases are precomputed power-of-two multiples of a single 121 | /// base. 122 | #[tracing::instrument(target = "r1cs", skip(scalar_bits_with_bases))] 123 | fn precomputed_base_scalar_mul_le<'a, I, B>( 124 | &mut self, 125 | scalar_bits_with_bases: I, 126 | ) -> Result<(), SynthesisError> 127 | where 128 | I: Iterator, 129 | B: Borrow>, 130 | C: 'a, 131 | { 132 | // Computes the standard little-endian double-and-add algorithm 133 | // (Algorithm 3.26, Guide to Elliptic Curve Cryptography) 134 | 135 | // Let `original` be the initial value of `self`. 136 | let mut result = Self::zero(); 137 | for (bit, base) in scalar_bits_with_bases { 138 | // Compute `self + 2^i * original` 139 | let self_plus_base = result.clone() + *base; 140 | // If `bit == 1`, set self = self + 2^i * original; 141 | // else, set self = self; 142 | result = bit.borrow().select(&self_plus_base, &result)?; 143 | } 144 | *self += result; 145 | Ok(()) 146 | } 147 | 148 | /// Computes `Σⱼ(scalarⱼ * baseⱼ)` for all j, 149 | /// where `scalarⱼ` is a `Boolean` *little-endian* 150 | /// representation of the j-th scalar. 151 | #[tracing::instrument(target = "r1cs", skip(bases, scalars))] 152 | fn precomputed_base_multiscalar_mul_le<'a, T, I, B>( 153 | bases: &[B], 154 | scalars: I, 155 | ) -> Result 156 | where 157 | T: 'a + ToBitsGadget + ?Sized, 158 | I: Iterator, 159 | B: Borrow<[C]>, 160 | { 161 | let mut result = Self::zero(); 162 | // Compute Σᵢ(bitᵢ * baseᵢ) for all i. 163 | for (bits, bases) in scalars.zip(bases) { 164 | let bases = bases.borrow(); 165 | let bits = bits.to_bits_le()?; 166 | result.precomputed_base_scalar_mul_le(bits.iter().zip(bases))?; 167 | } 168 | Ok(result) 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/uint/test_utils.rs: -------------------------------------------------------------------------------- 1 | use ark_relations::r1cs::{ConstraintSystem, SynthesisError}; 2 | use std::ops::RangeInclusive; 3 | 4 | use crate::test_utils::{self, modes}; 5 | 6 | use super::*; 7 | 8 | pub(crate) fn test_unary_op( 9 | a: T, 10 | mode: AllocationMode, 11 | test: impl FnOnce(UInt) -> Result<(), SynthesisError>, 12 | ) -> Result<(), SynthesisError> { 13 | let cs = ConstraintSystem::::new_ref(); 14 | let a = UInt::::new_variable(cs.clone(), || Ok(a), mode)?; 15 | test(a) 16 | } 17 | 18 | pub(crate) fn test_binary_op( 19 | a: T, 20 | b: T, 21 | mode_a: AllocationMode, 22 | mode_b: AllocationMode, 23 | test: impl FnOnce(UInt, UInt) -> Result<(), SynthesisError>, 24 | ) -> Result<(), SynthesisError> { 25 | let cs = ConstraintSystem::::new_ref(); 26 | let a = UInt::::new_variable(cs.clone(), || Ok(a), mode_a)?; 27 | let b = UInt::::new_variable(cs.clone(), || Ok(b), mode_b)?; 28 | test(a, b) 29 | } 30 | 31 | pub(crate) fn test_binary_op_with_native( 32 | a: T, 33 | b: T, 34 | mode_a: AllocationMode, 35 | test: impl FnOnce(UInt, T) -> Result<(), SynthesisError>, 36 | ) -> Result<(), SynthesisError> { 37 | let cs = ConstraintSystem::::new_ref(); 38 | let a = UInt::::new_variable(cs.clone(), || Ok(a), mode_a)?; 39 | test(a, b) 40 | } 41 | 42 | pub(crate) fn run_binary_random_both( 43 | test: impl Fn(UInt, UInt) -> Result<(), SynthesisError> + Copy, 44 | test_native: impl Fn(UInt, T) -> Result<(), SynthesisError> + Copy, 45 | ) -> Result<(), SynthesisError> 46 | where 47 | T: PrimUInt, 48 | F: PrimeField, 49 | { 50 | let mut rng = ark_std::test_rng(); 51 | 52 | for _ in 0..ITERATIONS { 53 | for mode_a in modes() { 54 | let a = T::rand(&mut rng); 55 | for mode_b in modes() { 56 | let b = T::rand(&mut rng); 57 | test_binary_op(a, b, mode_a, mode_b, test)?; 58 | test_binary_op_with_native(a, b, mode_a, test_native)?; 59 | } 60 | } 61 | } 62 | Ok(()) 63 | } 64 | 65 | pub(crate) fn run_binary_random( 66 | test: impl Fn(UInt, UInt) -> Result<(), SynthesisError> + Copy, 67 | ) -> Result<(), SynthesisError> 68 | where 69 | T: PrimUInt, 70 | F: PrimeField, 71 | { 72 | let mut rng = ark_std::test_rng(); 73 | 74 | for _ in 0..ITERATIONS { 75 | for mode_a in modes() { 76 | let a = T::rand(&mut rng); 77 | for mode_b in modes() { 78 | let b = T::rand(&mut rng); 79 | test_binary_op(a, b, mode_a, mode_b, test)?; 80 | } 81 | } 82 | } 83 | Ok(()) 84 | } 85 | 86 | pub(crate) fn run_binary_exhaustive( 87 | test: impl Fn(UInt, UInt) -> Result<(), SynthesisError> + Copy, 88 | ) -> Result<(), SynthesisError> 89 | where 90 | T: PrimUInt, 91 | F: PrimeField, 92 | RangeInclusive: Iterator, 93 | { 94 | for (mode_a, a) in test_utils::combination(T::min_value()..=T::max_value()) { 95 | for (mode_b, b) in test_utils::combination(T::min_value()..=T::max_value()) { 96 | test_binary_op(a, b, mode_a, mode_b, test)?; 97 | } 98 | } 99 | Ok(()) 100 | } 101 | 102 | pub(crate) fn run_binary_exhaustive_both( 103 | test: impl Fn(UInt, UInt) -> Result<(), SynthesisError> + Copy, 104 | test_native: impl Fn(UInt, T) -> Result<(), SynthesisError> + Copy, 105 | ) -> Result<(), SynthesisError> 106 | where 107 | T: PrimUInt, 108 | F: PrimeField, 109 | RangeInclusive: Iterator, 110 | { 111 | for (mode_a, a) in test_utils::combination(T::min_value()..=T::max_value()) { 112 | for (mode_b, b) in test_utils::combination(T::min_value()..=T::max_value()) { 113 | test_binary_op(a, b, mode_a, mode_b, test)?; 114 | test_binary_op_with_native(a, b, mode_a, test_native)?; 115 | } 116 | } 117 | Ok(()) 118 | } 119 | 120 | pub(crate) fn run_binary_random_native_only( 121 | test: impl Fn(UInt, T) -> Result<(), SynthesisError> + Copy, 122 | ) -> Result<(), SynthesisError> 123 | where 124 | T: PrimUInt, 125 | F: PrimeField, 126 | { 127 | let mut rng = ark_std::test_rng(); 128 | 129 | for _ in 0..ITERATIONS { 130 | for mode_a in modes() { 131 | let a = T::rand(&mut rng); 132 | let b = T::rand(&mut rng); 133 | test_binary_op_with_native(a, b, mode_a, test)?; 134 | } 135 | } 136 | Ok(()) 137 | } 138 | 139 | pub(crate) fn run_binary_exhaustive_native_only( 140 | test: impl Fn(UInt, T) -> Result<(), SynthesisError> + Copy, 141 | ) -> Result<(), SynthesisError> 142 | where 143 | T: PrimUInt, 144 | F: PrimeField, 145 | RangeInclusive: Iterator, 146 | { 147 | for (mode_a, a) in test_utils::combination(T::min_value()..=T::max_value()) { 148 | for b in T::min_value()..=T::max_value() { 149 | test_binary_op_with_native(a, b, mode_a, test)?; 150 | } 151 | } 152 | Ok(()) 153 | } 154 | 155 | pub(crate) fn run_unary_random( 156 | test: impl Fn(UInt) -> Result<(), SynthesisError> + Copy, 157 | ) -> Result<(), SynthesisError> 158 | where 159 | T: PrimUInt, 160 | F: PrimeField, 161 | { 162 | let mut rng = ark_std::test_rng(); 163 | 164 | for _ in 0..ITERATIONS { 165 | for mode_a in modes() { 166 | let a = T::rand(&mut rng); 167 | test_unary_op(a, mode_a, test)?; 168 | } 169 | } 170 | Ok(()) 171 | } 172 | 173 | pub(crate) fn run_unary_exhaustive( 174 | test: impl Fn(UInt) -> Result<(), SynthesisError> + Copy, 175 | ) -> Result<(), SynthesisError> 176 | where 177 | T: PrimUInt, 178 | F: PrimeField, 179 | RangeInclusive: Iterator, 180 | { 181 | for (mode, a) in test_utils::combination(T::min_value()..=T::max_value()) { 182 | test_unary_op(a, mode, test)?; 183 | } 184 | Ok(()) 185 | } 186 | -------------------------------------------------------------------------------- /src/fields/fp12.rs: -------------------------------------------------------------------------------- 1 | use crate::fields::{fp2::Fp2Var, fp6_3over2::Fp6Var, quadratic_extension::*, FieldVar}; 2 | use ark_ff::{ 3 | fields::{fp12_2over3over2::*, Field}, 4 | fp6_3over2::Fp6Config, 5 | QuadExtConfig, 6 | }; 7 | use ark_relations::r1cs::SynthesisError; 8 | 9 | /// A degree-12 extension field constructed as the tower of a 10 | /// quadratic extension over a cubic extension over a quadratic extension field. 11 | /// This is the R1CS equivalent of `ark_ff::fp12_2over3over2::Fp12

`. 12 | pub type Fp12Var

= QuadExtVar::Fp6Config>, Fp12ConfigWrapper

>; 13 | 14 | type Fp2Config

= <

::Fp6Config as Fp6Config>::Fp2Config; 15 | 16 | impl QuadExtVarConfig> for Fp12ConfigWrapper

{ 17 | fn mul_base_field_var_by_frob_coeff(fe: &mut Fp6Var, power: usize) { 18 | fe.c0 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; 19 | fe.c1 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; 20 | fe.c2 *= Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD]; 21 | } 22 | } 23 | 24 | impl Fp12Var

{ 25 | /// Multiplies by a sparse element of the form `(c0 = (c0, c1, 0), c1 = (0, 26 | /// d1, 0))`. 27 | #[inline] 28 | pub fn mul_by_014( 29 | &self, 30 | c0: &Fp2Var>, 31 | c1: &Fp2Var>, 32 | d1: &Fp2Var>, 33 | ) -> Result { 34 | let v0 = self.c0.mul_by_c0_c1_0(&c0, &c1)?; 35 | let v1 = self.c1.mul_by_0_c1_0(&d1)?; 36 | let new_c0 = Self::mul_base_field_by_nonresidue(&v1)? + &v0; 37 | 38 | let new_c1 = (&self.c0 + &self.c1).mul_by_c0_c1_0(&c0, &(c1 + d1))? - &v0 - &v1; 39 | Ok(Self::new(new_c0, new_c1)) 40 | } 41 | 42 | /// Multiplies by a sparse element of the form `(c0 = (c0, 0, 0), c1 = (d0, 43 | /// d1, 0))`. 44 | #[inline] 45 | pub fn mul_by_034( 46 | &self, 47 | c0: &Fp2Var>, 48 | d0: &Fp2Var>, 49 | d1: &Fp2Var>, 50 | ) -> Result { 51 | let a0 = &self.c0.c0 * c0; 52 | let a1 = &self.c0.c1 * c0; 53 | let a2 = &self.c0.c2 * c0; 54 | let a = Fp6Var::new(a0, a1, a2); 55 | let b = self.c1.mul_by_c0_c1_0(&d0, &d1)?; 56 | 57 | let c0 = c0 + d0; 58 | let c1 = d1; 59 | let e = (&self.c0 + &self.c1).mul_by_c0_c1_0(&c0, &c1)?; 60 | let new_c1 = e - (&a + &b); 61 | let new_c0 = Self::mul_base_field_by_nonresidue(&b)? + &a; 62 | 63 | Ok(Self::new(new_c0, new_c1)) 64 | } 65 | 66 | /// Squares `self` when `self` is in the cyclotomic subgroup. 67 | pub fn cyclotomic_square(&self) -> Result { 68 | if characteristic_square_mod_6_is_one(Fp12::

::characteristic()) { 69 | let fp2_nr = ::NONRESIDUE; 70 | 71 | let z0 = &self.c0.c0; 72 | let z4 = &self.c0.c1; 73 | let z3 = &self.c0.c2; 74 | let z2 = &self.c1.c0; 75 | let z1 = &self.c1.c1; 76 | let z5 = &self.c1.c2; 77 | 78 | // t0 + t1*y = (z0 + z1*y)^2 = a^2 79 | let tmp = z0 * z1; 80 | let t0 = { 81 | let tmp1 = z0 + z1; 82 | let tmp2 = z1 * fp2_nr + z0; 83 | let tmp4 = &tmp * fp2_nr + &tmp; 84 | tmp1 * tmp2 - tmp4 85 | }; 86 | let t1 = tmp.double()?; 87 | 88 | // t2 + t3*y = (z2 + z3*y)^2 = b^2 89 | let tmp = z2 * z3; 90 | let t2 = { 91 | // (z2 + &z3) * &(z2 + &(fp2_nr * &z3)) - &tmp - &(tmp * &fp2_nr); 92 | let tmp1 = z2 + z3; 93 | let tmp2 = z3 * fp2_nr + z2; 94 | let tmp4 = &tmp * fp2_nr + &tmp; 95 | tmp1 * tmp2 - tmp4 96 | }; 97 | let t3 = tmp.double()?; 98 | 99 | // t4 + t5*y = (z4 + z5*y)^2 = c^2 100 | let tmp = z4 * z5; 101 | let t4 = { 102 | // (z4 + &z5) * &(z4 + &(fp2_nr * &z5)) - &tmp - &(tmp * &fp2_nr); 103 | let tmp1 = z4 + z5; 104 | let tmp2 = (z5 * fp2_nr) + z4; 105 | let tmp4 = (&tmp * fp2_nr) + &tmp; 106 | (tmp1 * tmp2) - tmp4 107 | }; 108 | let t5 = tmp.double()?; 109 | 110 | // for A 111 | 112 | // z0 = 3 * t0 - 2 * z0 113 | let c0_c0 = (&t0 - z0).double()? + &t0; 114 | 115 | // z1 = 3 * t1 + 2 * z1 116 | let c1_c1 = (&t1 + z1).double()? + &t1; 117 | 118 | // for B 119 | 120 | // z2 = 3 * (xi * t5) + 2 * z2 121 | let c1_c0 = { 122 | let tmp = &t5 * fp2_nr; 123 | (z2 + &tmp).double()? + &tmp 124 | }; 125 | 126 | // z3 = 3 * t4 - 2 * z3 127 | let c0_c2 = (&t4 - z3).double()? + &t4; 128 | 129 | // for C 130 | 131 | // z4 = 3 * t2 - 2 * z4 132 | let c0_c1 = (&t2 - z4).double()? + &t2; 133 | 134 | // z5 = 3 * t3 + 2 * z5 135 | let c1_c2 = (&t3 + z5).double()? + &t3; 136 | let c0 = Fp6Var::new(c0_c0, c0_c1, c0_c2); 137 | let c1 = Fp6Var::new(c1_c0, c1_c1, c1_c2); 138 | 139 | Ok(Self::new(c0, c1)) 140 | } else { 141 | self.square() 142 | } 143 | } 144 | 145 | /// Like `Self::cyclotomic_exp`, but additionally uses cyclotomic squaring. 146 | pub fn optimized_cyclotomic_exp( 147 | &self, 148 | exponent: impl AsRef<[u64]>, 149 | ) -> Result { 150 | use ark_ff::biginteger::arithmetic::find_naf; 151 | let mut res = Self::one(); 152 | let self_inverse = self.unitary_inverse()?; 153 | 154 | let mut found_nonzero = false; 155 | let naf = find_naf(exponent.as_ref()); 156 | 157 | for &value in naf.iter().rev() { 158 | if found_nonzero { 159 | res = res.cyclotomic_square()?; 160 | } 161 | 162 | if value != 0 { 163 | found_nonzero = true; 164 | 165 | if value > 0 { 166 | res *= self; 167 | } else { 168 | res *= &self_inverse; 169 | } 170 | } 171 | } 172 | 173 | Ok(res) 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/boolean/or.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use ark_relations::r1cs::SynthesisError; 3 | use ark_std::{ops::BitOr, ops::BitOrAssign}; 4 | 5 | use crate::{ 6 | eq::EqGadget, 7 | fields::{fp::FpVar, FieldVar}, 8 | }; 9 | 10 | use super::Boolean; 11 | 12 | impl Boolean { 13 | fn _or(&self, other: &Self) -> Result { 14 | use Boolean::*; 15 | match (self, other) { 16 | (&Constant(false), x) | (x, &Constant(false)) => Ok(x.clone()), 17 | (&Constant(true), _) | (_, &Constant(true)) => Ok(Constant(true)), 18 | (Var(ref x), Var(ref y)) => Ok(Var(x.or(y)?)), 19 | } 20 | } 21 | 22 | /// Outputs `bits[0] | bits[1] | ... | bits.last().unwrap()`. 23 | /// 24 | /// ``` 25 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 26 | /// // We'll use the BLS12-381 scalar field for our constraints. 27 | /// use ark_test_curves::bls12_381::Fr; 28 | /// use ark_relations::r1cs::*; 29 | /// use ark_r1cs_std::prelude::*; 30 | /// 31 | /// let cs = ConstraintSystem::::new_ref(); 32 | /// 33 | /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; 34 | /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; 35 | /// let c = Boolean::new_witness(cs.clone(), || Ok(false))?; 36 | /// 37 | /// Boolean::kary_or(&[a.clone(), b.clone(), c.clone()])?.enforce_equal(&Boolean::TRUE)?; 38 | /// Boolean::kary_or(&[a.clone(), c.clone()])?.enforce_equal(&Boolean::TRUE)?; 39 | /// Boolean::kary_or(&[b.clone(), c.clone()])?.enforce_equal(&Boolean::FALSE)?; 40 | /// 41 | /// assert!(cs.is_satisfied().unwrap()); 42 | /// # Ok(()) 43 | /// # } 44 | /// ``` 45 | #[tracing::instrument(target = "r1cs")] 46 | pub fn kary_or(bits: &[Self]) -> Result { 47 | assert!(!bits.is_empty()); 48 | if bits.len() <= 3 { 49 | let mut cur: Option = None; 50 | for next in bits { 51 | cur = if let Some(b) = cur { 52 | Some(b | next) 53 | } else { 54 | Some(next.clone()) 55 | }; 56 | } 57 | 58 | Ok(cur.expect("should not be 0")) 59 | } else { 60 | // b0 | b1 | ... | bN == 1 if and only if not all of b0, b1, ..., bN are 0. 61 | // We can enforce this by requiring that the sum of b0, b1, ..., bN is not 0. 62 | let sum_bits: FpVar<_> = bits.iter().map(|b| FpVar::from(b.clone())).sum(); 63 | sum_bits.is_neq(&FpVar::zero()) 64 | } 65 | } 66 | } 67 | 68 | impl<'a, F: PrimeField> BitOr for &'a Boolean { 69 | type Output = Boolean; 70 | 71 | /// Outputs `self | other`. 72 | /// 73 | /// If at least one of `self` and `other` are constants, then this method 74 | /// *does not* create any constraints or variables. 75 | /// 76 | /// ``` 77 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 78 | /// // We'll use the BLS12-381 scalar field for our constraints. 79 | /// use ark_test_curves::bls12_381::Fr; 80 | /// use ark_relations::r1cs::*; 81 | /// use ark_r1cs_std::prelude::*; 82 | /// 83 | /// let cs = ConstraintSystem::::new_ref(); 84 | /// 85 | /// let a = Boolean::new_witness(cs.clone(), || Ok(true))?; 86 | /// let b = Boolean::new_witness(cs.clone(), || Ok(false))?; 87 | /// 88 | /// (&a | &b).enforce_equal(&Boolean::TRUE)?; 89 | /// (&b | &a).enforce_equal(&Boolean::TRUE)?; 90 | /// 91 | /// (&a | &a).enforce_equal(&Boolean::TRUE)?; 92 | /// (&b | &b).enforce_equal(&Boolean::FALSE)?; 93 | /// 94 | /// assert!(cs.is_satisfied().unwrap()); 95 | /// # Ok(()) 96 | /// # } 97 | /// ``` 98 | #[tracing::instrument(target = "r1cs", skip(self, other))] 99 | fn bitor(self, other: Self) -> Self::Output { 100 | self._or(other).unwrap() 101 | } 102 | } 103 | 104 | impl<'a, F: PrimeField> BitOr<&'a Self> for Boolean { 105 | type Output = Boolean; 106 | 107 | #[tracing::instrument(target = "r1cs", skip(self, other))] 108 | fn bitor(self, other: &Self) -> Self::Output { 109 | self._or(&other).unwrap() 110 | } 111 | } 112 | 113 | impl<'a, F: PrimeField> BitOr> for &'a Boolean { 114 | type Output = Boolean; 115 | 116 | #[tracing::instrument(target = "r1cs", skip(self, other))] 117 | fn bitor(self, other: Boolean) -> Self::Output { 118 | self._or(&other).unwrap() 119 | } 120 | } 121 | 122 | impl BitOr for Boolean { 123 | type Output = Self; 124 | 125 | #[tracing::instrument(target = "r1cs", skip(self, other))] 126 | fn bitor(self, other: Self) -> Self::Output { 127 | self._or(&other).unwrap() 128 | } 129 | } 130 | 131 | impl BitOrAssign for Boolean { 132 | /// Sets `self = self | other`. 133 | #[tracing::instrument(target = "r1cs", skip(self, other))] 134 | fn bitor_assign(&mut self, other: Self) { 135 | let result = self._or(&other).unwrap(); 136 | *self = result; 137 | } 138 | } 139 | 140 | impl<'a, F: PrimeField> BitOrAssign<&'a Self> for Boolean { 141 | /// Sets `self = self | other`. 142 | #[tracing::instrument(target = "r1cs", skip(self, other))] 143 | fn bitor_assign(&mut self, other: &'a Self) { 144 | let result = self._or(other).unwrap(); 145 | *self = result; 146 | } 147 | } 148 | 149 | #[cfg(test)] 150 | mod tests { 151 | use super::*; 152 | use crate::{ 153 | alloc::{AllocVar, AllocationMode}, 154 | boolean::test_utils::run_binary_exhaustive, 155 | prelude::EqGadget, 156 | R1CSVar, 157 | }; 158 | use ark_test_curves::bls12_381::Fr; 159 | 160 | #[test] 161 | fn or() { 162 | run_binary_exhaustive::(|a, b| { 163 | let cs = a.cs().or(b.cs()); 164 | let both_constant = a.is_constant() && b.is_constant(); 165 | let computed = &a | &b; 166 | let expected_mode = if both_constant { 167 | AllocationMode::Constant 168 | } else { 169 | AllocationMode::Witness 170 | }; 171 | let expected = 172 | Boolean::new_variable(cs.clone(), || Ok(a.value()? | b.value()?), expected_mode)?; 173 | assert_eq!(expected.value(), computed.value()); 174 | expected.enforce_equal(&computed)?; 175 | if !both_constant { 176 | assert!(cs.is_satisfied().unwrap()); 177 | } 178 | Ok(()) 179 | }) 180 | .unwrap() 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/poly/domain/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | boolean::Boolean, 3 | eq::EqGadget, 4 | fields::{fp::FpVar, FieldVar}, 5 | }; 6 | use ark_ff::PrimeField; 7 | use ark_relations::r1cs::SynthesisError; 8 | use ark_std::vec::Vec; 9 | 10 | pub mod vanishing_poly; 11 | 12 | #[derive(Clone, Debug)] 13 | /// Defines an evaluation domain over a prime field. The domain is a coset of 14 | /// size `1< { 20 | /// generator of subgroup g 21 | pub gen: F, 22 | /// index of the quotient group (i.e. the `offset`) 23 | offset: FpVar, 24 | /// dimension of evaluation domain, which is log2(size of coset) 25 | pub dim: u64, 26 | } 27 | impl Radix2DomainVar { 28 | /// Construct an evaluation domain with the given offset. 29 | pub fn new(gen: F, dimension: u64, offset: FpVar) -> Result { 30 | offset.enforce_not_equal(&FpVar::zero())?; 31 | Ok(Self { 32 | gen, 33 | offset, 34 | dim: dimension, 35 | }) 36 | } 37 | 38 | /// What is the offset of `self`? 39 | pub fn offset(&self) -> &FpVar { 40 | &self.offset 41 | } 42 | } 43 | 44 | impl EqGadget for Radix2DomainVar { 45 | fn is_eq(&self, other: &Self) -> Result, SynthesisError> { 46 | if self.gen != other.gen || self.dim != other.dim { 47 | Ok(Boolean::FALSE) 48 | } else { 49 | self.offset.is_eq(&other.offset) 50 | } 51 | } 52 | } 53 | 54 | impl Radix2DomainVar { 55 | /// order of the domain 56 | pub fn order(&self) -> usize { 57 | 1 << self.dim 58 | } 59 | 60 | /// Returns offset, offset*g, offset*g^2, ..., offset*g^{coset_size} 61 | pub fn elements(&self) -> Vec> { 62 | let mut result = Vec::new(); 63 | result.push(self.offset.clone()); 64 | for _ in 1..(1 << self.dim) { 65 | let new_element = result.last().unwrap() * self.gen; 66 | result.push(new_element); 67 | } 68 | result 69 | } 70 | 71 | /// Size of the domain 72 | pub fn size(&self) -> u64 { 73 | 1 << self.dim 74 | } 75 | 76 | /// For domain `h` with dimension `n`, `position` represented by 77 | /// `query_pos` in big endian form, returns all points of 78 | /// `h*g^{position}`. The result is the query coset at 79 | /// index `query_pos` for the FRI protocol. 80 | /// 81 | /// # Panics 82 | /// This function panics when `query_pos.len() != coset_dim` or 83 | /// `query_pos.len() != self.dim`. 84 | pub fn query_position_to_coset_elements( 85 | &self, 86 | query_pos: &[Boolean], 87 | coset_dim: u64, 88 | ) -> Result>, SynthesisError> { 89 | Ok(self 90 | .query_position_to_coset(query_pos, coset_dim)? 91 | .elements()) 92 | } 93 | 94 | /// For domain `h` with dimension `n`, `position` represented by 95 | /// `query_pos` in big endian form, returns all points of 96 | /// `h*g^{position}` 97 | /// 98 | /// # Panics 99 | /// This function panics when `query_pos.len() < log2_num_cosets`. 100 | pub fn query_position_to_coset( 101 | &self, 102 | query_pos: &[Boolean], 103 | coset_dim: u64, 104 | ) -> Result { 105 | let coset_index = truncate_to_coset_index(query_pos, self.dim, coset_dim); 106 | let offset_var = &self.offset * FpVar::Constant(self.gen).pow_le(&coset_index)?; 107 | Ok(Self { 108 | gen: self.gen.pow(&[1 << (self.dim - coset_dim)]), // distance between coset 109 | offset: offset_var, 110 | dim: coset_dim, 111 | }) 112 | } 113 | } 114 | 115 | fn truncate_to_coset_index( 116 | query_pos: &[Boolean], 117 | codeword_dim: u64, 118 | coset_dim: u64, 119 | ) -> Vec> { 120 | let log2_num_cosets = (codeword_dim - coset_dim) as usize; 121 | assert!(query_pos.len() >= log2_num_cosets); 122 | query_pos[0..log2_num_cosets].to_vec() 123 | } 124 | 125 | #[cfg(test)] 126 | mod tests { 127 | use crate::prelude::*; 128 | use ark_ff::PrimeField; 129 | use ark_relations::r1cs::ConstraintSystem; 130 | use ark_std::{rand::Rng, test_rng}; 131 | 132 | use crate::{ 133 | alloc::AllocVar, convert::ToBitsGadget, fields::fp::FpVar, poly::domain::Radix2DomainVar, 134 | R1CSVar, 135 | }; 136 | 137 | fn test_query_coset_template() { 138 | const COSET_DIM: u64 = 7; 139 | const COSET_SIZE: u64 = 1 << COSET_DIM; 140 | const LOCALIZATION: u64 = 3; 141 | let cs = ConstraintSystem::new_ref(); 142 | let mut rng = test_rng(); 143 | let gen = F::get_root_of_unity(COSET_SIZE).unwrap(); 144 | let offset = F::rand(&mut rng); 145 | let offset_var = FpVar::new_witness(cs.clone(), || Ok(offset)).unwrap(); 146 | let domain = Radix2DomainVar::new(gen, COSET_DIM, offset_var).unwrap(); 147 | 148 | let num_cosets = 1 << (COSET_DIM - LOCALIZATION); 149 | 150 | let coset_index = rng.gen_range(0..num_cosets); 151 | println!("{:0b}", coset_index); 152 | let coset_index_var = UInt32::new_witness(cs.clone(), || Ok(coset_index)) 153 | .unwrap() 154 | .to_bits_le() 155 | .unwrap() 156 | .into_iter() 157 | .take(COSET_DIM as usize) 158 | .collect::>(); 159 | 160 | let elements_actual = domain 161 | .query_position_to_coset(&coset_index_var, LOCALIZATION) 162 | .unwrap() 163 | .elements(); 164 | 165 | let elements_expected = domain 166 | .elements() 167 | .into_iter() 168 | .skip(coset_index as usize) 169 | .step_by(1 << (COSET_DIM - LOCALIZATION)) 170 | .collect::>(); 171 | 172 | assert_eq!(elements_expected.len(), elements_actual.len()); 173 | 174 | elements_expected 175 | .into_iter() 176 | .zip(elements_actual.into_iter()) 177 | .for_each(|(left, right)| { 178 | assert_eq!(left.value().unwrap(), right.value().unwrap()); 179 | }); 180 | } 181 | 182 | #[test] 183 | fn test_on_bls12_381() { 184 | test_query_coset_template::(); 185 | } 186 | 187 | #[test] 188 | fn test_on_bls12_377() { 189 | test_query_coset_template::(); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## Pending 4 | 5 | ### Breaking changes 6 | 7 | - [\#121](https://github.com/arkworks-rs/r1cs-std/pull/121) 8 | - Refactor `UInt{8,16,64,128}` into one struct `UInt`. 9 | - Remove `bits` module. 10 | - Use `std::ops` traits for `UInt` and `Boolean`. 11 | - [\#134](https://github.com/arkworks-rs/r1cs-std/pull/134) Add `Mul` bounds and impls for `CurveVar`. 12 | - [\#135](https://github.com/arkworks-rs/r1cs-std/pull/135) 13 | - Rename `NonNativeFieldVar` to `EmulatedFpVar`. 14 | - Rename `fields::nonnative` to `fields::emulated_fp`. 15 | - Rename `fields::nonnative::{Allocated}NonNativeMulResultVar` to `fields::emulated_fp::{Allocated}MulResultVar`. 16 | - [\#136](https://github.com/arkworks-rs/r1cs-std/pull/136) 17 | - Rename `ToBytesGadget::to_{non_unique_}bytes` → `ToBytesGadget::to_{non_unique_}bytes_in_le`. 18 | 19 | ### Features 20 | 21 | - [\#136](https://github.com/arkworks-rs/r1cs-std/pull/136) 22 | - Add `{BitAnd,BitOr,BitXor,BitAndAssign,BitOrAssign,BitXorAssign} for UInt`. 23 | - Add `UInt::rotate_{left,right}_in_place`. 24 | - Add `{Boolean,UInt}::not_in_place`. 25 | - Add `UInt::{from_bytes_le, from_bytes_be, to_bytes_be}`. 26 | 27 | ### Improvements 28 | 29 | ### Bug Fixes 30 | 31 | ## 0.4.0 32 | 33 | - [\#117](https://github.com/arkworks-rs/r1cs-std/pull/117) Fix result of `precomputed_base_scalar_mul_le` to not discard previous value. 34 | - [\#124](https://github.com/arkworks-rs/r1cs-std/pull/124) Fix `scalar_mul_le` constraints unsatisfiability when short Weierstrass point is zero. 35 | - [\#127](https://github.com/arkworks-rs/r1cs-std/pull/127) Convert `NonNativeFieldVar` constants to little-endian bytes instead of big-endian (`ToBytesGadget`). 36 | - [\#133](https://github.com/arkworks-rs/r1cs-std/pull/133) Save 1 constraint in `FpVar::{is_eq, is_neq}` by removing the unnecessary booleanity check. 37 | 38 | ### Breaking changes 39 | 40 | - [\#86](https://github.com/arkworks-rs/r1cs-std/pull/86) Change the API for domains for coset. 41 | 42 | ### Features 43 | 44 | - [\#84](https://github.com/arkworks-rs/r1cs-std/pull/84) Expose `short_weierstrass::non_zero_affine` module 45 | and implement `EqGadget` for `NonZeroAffineVar`. 46 | - [\#79](https://github.com/arkworks-rs/r1cs-std/pull/79) Move `NonNativeFieldVar` from `ark-nonnative` to `ark-r1cs-std`. 47 | - [\#76](https://github.com/arkworks-rs/r1cs-std/pull/76) Implement `ToBytesGadget` for `Vec`. 48 | - [nonnative/\#45](https://github.com/arkworks-rs/nonnative/pull/45) Add `new_witness_with_le_bits` which returns the bits used during variable allocation. 49 | 50 | ### Improvements 51 | 52 | ### Bug Fixes 53 | 54 | - [\#101](https://github.com/arkworks-rs/r1cs-std/pull/101) Fix `is_zero` for twisted Edwards curves. 55 | - [\#86](https://github.com/arkworks-rs/r1cs-std/pull/86) Make result of `query_position_to_coset` consistent with `ark-ldt`. 56 | - [\#77](https://github.com/arkworks-rs/r1cs-std/pull/77) Fix BLS12 `G2PreparedGadget`'s `AllocVar` when G2 uses a divisive twist. 57 | 58 | ## v0.3.1 59 | 60 | ### Features 61 | 62 | - [\#71](https://github.com/arkworks-rs/r1cs-std/pull/71) Implement the `Sum` trait for `FpVar`. 63 | - [\#75](https://github.com/arkworks-rs/r1cs-std/pull/71) Introduce `mul_by_inverse_unchecked` for `FieldVar`. This accompanies the bug fix in [\#70](https://github.com/arkworks-rs/r1cs-std/pull/70). 64 | 65 | ### Bug Fixes 66 | 67 | - [\#70](https://github.com/arkworks-rs/r1cs-std/pull/70) Fix soundness issues of `mul_by_inverse` for field gadgets. 68 | 69 | ## v0.3.0 70 | 71 | ### Breaking changes 72 | 73 | - [\#60](https://github.com/arkworks-rs/r1cs-std/pull/60) Rename `AllocatedBit` to `AllocatedBool` for consistency with the `Boolean` variable. 74 | You can update downstream usage with `grep -rl 'AllocatedBit' . | xargs env LANG=C env LC_CTYPE=C sed -i '' 's/AllocatedBit/AllocatedBool/g'`. 75 | - [\#65](https://github.com/arkworks-rs/r1cs-std/pull/65) Rename `Radix2Domain` in `r1cs-std` to `Radix2DomainVar`. 76 | - [nonnative/\#43](https://github.com/arkworks-rs/nonnative/pull/43) Add padding to allocated nonnative element's `to_bytes`. 77 | 78 | ### Features 79 | 80 | - [\#53](https://github.com/arkworks-rs/r1cs-std/pull/53) Add univariate evaluation domain and Lagrange interpolation. 81 | 82 | ### Improvements 83 | 84 | - [\#65](https://github.com/arkworks-rs/r1cs-std/pull/65) Add support for non-constant coset offset in `Radix2DomainVar`. 85 | 86 | ### Bug Fixes 87 | 88 | ## v0.2.0 89 | 90 | ### Breaking changes 91 | 92 | - [\#12](https://github.com/arkworks-rs/r1cs-std/pull/12) Make the output of the `ToBitsGadget` impl for `FpVar` fixed-size 93 | - [\#48](https://github.com/arkworks-rs/r1cs-std/pull/48) Add `Clone` trait bound to `CondSelectGadget`. 94 | 95 | ### Features 96 | 97 | - [\#21](https://github.com/arkworks-rs/r1cs-std/pull/21) Add `UInt128` 98 | - [\#50](https://github.com/arkworks-rs/r1cs-std/pull/50) Add `DensePolynomialVar` 99 | 100 | ### Improvements 101 | 102 | - [\#5](https://github.com/arkworks-rs/r1cs-std/pull/5) Speedup BLS-12 pairing 103 | - [\#13](https://github.com/arkworks-rs/r1cs-std/pull/13) Add `ToConstraintFieldGadget` to `ProjectiveVar` 104 | - [\#15](https://github.com/arkworks-rs/r1cs-std/pull/15), #16 Allow `cs` to be `None` when converting a Montgomery point into a Twisted Edwards point 105 | - [\#20](https://github.com/arkworks-rs/r1cs-std/pull/20) Add `CondSelectGadget` impl for `UInt`s 106 | - [\#22](https://github.com/arkworks-rs/r1cs-std/pull/22) Reduce density of `three_bit_cond_neg_lookup` 107 | - [\#23](https://github.com/arkworks-rs/r1cs-std/pull/23) Reduce allocations in `UInt`s 108 | - [\#33](https://github.com/arkworks-rs/r1cs-std/pull/33) Speedup scalar multiplication by a constant 109 | - [\#35](https://github.com/arkworks-rs/r1cs-std/pull/35) Construct a `FpVar` from bits 110 | - [\#36](https://github.com/arkworks-rs/r1cs-std/pull/36) Implement `ToConstraintFieldGadget` for `Vec` 111 | - [\#40](https://github.com/arkworks-rs/r1cs-std/pull/40), #43 Faster scalar multiplication for Short Weierstrass curves by relying on affine formulae 112 | - [\#46](https://github.com/arkworks-rs/r1cs-std/pull/46) Add mux gadget as an auto-impl in `CondSelectGadget` to support random access of an array 113 | 114 | ### Bug fixes 115 | 116 | - [\#8](https://github.com/arkworks-rs/r1cs-std/pull/8) Fix bug in `three_bit_cond_neg_lookup` when using a constant lookup bit 117 | - [\#9](https://github.com/arkworks-rs/r1cs-std/pull/9) Fix bug in `short_weierstrass::ProjectiveVar::to_affine` 118 | - [\#29](https://github.com/arkworks-rs/r1cs-std/pull/29) Fix `to_non_unique_bytes` for `BLS12::G1Prepared` 119 | - [\#34](https://github.com/arkworks-rs/r1cs-std/pull/34) Fix `mul_by_inverse` for constants 120 | - [\#42](https://github.com/arkworks-rs/r1cs-std/pull/42) Fix regression in `mul_by_inverse` constraint count 121 | - [\#47](https://github.com/arkworks-rs/r1cs-std/pull/47) Compile with `panic='abort'` in release mode, for safety of the library across FFI boundaries 122 | - [\#57](https://github.com/arkworks-rs/r1cs-std/pull/57) Clean up `UInt` docs 123 | 124 | ## v0.1.0 125 | 126 | Initial release 127 | -------------------------------------------------------------------------------- /src/uint/cmp.rs: -------------------------------------------------------------------------------- 1 | use crate::cmp::CmpGadget; 2 | 3 | use super::*; 4 | 5 | impl> CmpGadget for UInt { 6 | fn is_ge(&self, other: &Self) -> Result, SynthesisError> { 7 | if N + 1 < ((F::MODULUS_BIT_SIZE - 1) as usize) { 8 | let a = self.to_fp()?; 9 | let b = other.to_fp()?; 10 | let (bits, _) = (a - b + F::from(T::max_value()) + F::one()) 11 | .to_bits_le_with_top_bits_zero(N + 1)?; 12 | Ok(bits.last().unwrap().clone()) 13 | } else { 14 | unimplemented!("bit sizes larger than modulus size not yet supported") 15 | } 16 | } 17 | } 18 | 19 | #[cfg(test)] 20 | mod tests { 21 | use super::*; 22 | use crate::{ 23 | alloc::{AllocVar, AllocationMode}, 24 | prelude::EqGadget, 25 | uint::test_utils::{run_binary_exhaustive, run_binary_random}, 26 | R1CSVar, 27 | }; 28 | use ark_ff::PrimeField; 29 | use ark_test_curves::bls12_381::Fr; 30 | 31 | fn uint_gt>( 32 | a: UInt, 33 | b: UInt, 34 | ) -> Result<(), SynthesisError> { 35 | let cs = a.cs().or(b.cs()); 36 | let both_constant = a.is_constant() && b.is_constant(); 37 | let expected_mode = if both_constant { 38 | AllocationMode::Constant 39 | } else { 40 | AllocationMode::Witness 41 | }; 42 | let computed = a.is_gt(&b)?; 43 | let expected = 44 | Boolean::new_variable(cs.clone(), || Ok(a.value()? > b.value()?), expected_mode)?; 45 | assert_eq!(expected.value(), computed.value()); 46 | expected.enforce_equal(&computed)?; 47 | if !both_constant { 48 | assert!(cs.is_satisfied().unwrap()); 49 | } 50 | Ok(()) 51 | } 52 | 53 | fn uint_lt>( 54 | a: UInt, 55 | b: UInt, 56 | ) -> Result<(), SynthesisError> { 57 | let cs = a.cs().or(b.cs()); 58 | let both_constant = a.is_constant() && b.is_constant(); 59 | let expected_mode = if both_constant { 60 | AllocationMode::Constant 61 | } else { 62 | AllocationMode::Witness 63 | }; 64 | let computed = a.is_lt(&b)?; 65 | let expected = 66 | Boolean::new_variable(cs.clone(), || Ok(a.value()? < b.value()?), expected_mode)?; 67 | assert_eq!(expected.value(), computed.value()); 68 | expected.enforce_equal(&computed)?; 69 | if !both_constant { 70 | assert!(cs.is_satisfied().unwrap()); 71 | } 72 | Ok(()) 73 | } 74 | 75 | fn uint_ge>( 76 | a: UInt, 77 | b: UInt, 78 | ) -> Result<(), SynthesisError> { 79 | let cs = a.cs().or(b.cs()); 80 | let both_constant = a.is_constant() && b.is_constant(); 81 | let expected_mode = if both_constant { 82 | AllocationMode::Constant 83 | } else { 84 | AllocationMode::Witness 85 | }; 86 | let computed = a.is_ge(&b)?; 87 | let expected = 88 | Boolean::new_variable(cs.clone(), || Ok(a.value()? >= b.value()?), expected_mode)?; 89 | assert_eq!(expected.value(), computed.value()); 90 | expected.enforce_equal(&computed)?; 91 | if !both_constant { 92 | assert!(cs.is_satisfied().unwrap()); 93 | } 94 | Ok(()) 95 | } 96 | 97 | fn uint_le>( 98 | a: UInt, 99 | b: UInt, 100 | ) -> Result<(), SynthesisError> { 101 | let cs = a.cs().or(b.cs()); 102 | let both_constant = a.is_constant() && b.is_constant(); 103 | let expected_mode = if both_constant { 104 | AllocationMode::Constant 105 | } else { 106 | AllocationMode::Witness 107 | }; 108 | let computed = a.is_le(&b)?; 109 | let expected = 110 | Boolean::new_variable(cs.clone(), || Ok(a.value()? <= b.value()?), expected_mode)?; 111 | assert_eq!(expected.value(), computed.value()); 112 | expected.enforce_equal(&computed)?; 113 | if !both_constant { 114 | assert!(cs.is_satisfied().unwrap()); 115 | } 116 | Ok(()) 117 | } 118 | 119 | #[test] 120 | fn u8_gt() { 121 | run_binary_exhaustive(uint_gt::).unwrap() 122 | } 123 | 124 | #[test] 125 | fn u16_gt() { 126 | run_binary_random::<1000, 16, _, _>(uint_gt::).unwrap() 127 | } 128 | 129 | #[test] 130 | fn u32_gt() { 131 | run_binary_random::<1000, 32, _, _>(uint_gt::).unwrap() 132 | } 133 | 134 | #[test] 135 | fn u64_gt() { 136 | run_binary_random::<1000, 64, _, _>(uint_gt::).unwrap() 137 | } 138 | 139 | #[test] 140 | fn u128_gt() { 141 | run_binary_random::<1000, 128, _, _>(uint_gt::).unwrap() 142 | } 143 | 144 | #[test] 145 | fn u8_lt() { 146 | run_binary_exhaustive(uint_lt::).unwrap() 147 | } 148 | 149 | #[test] 150 | fn u16_lt() { 151 | run_binary_random::<1000, 16, _, _>(uint_lt::).unwrap() 152 | } 153 | 154 | #[test] 155 | fn u32_lt() { 156 | run_binary_random::<1000, 32, _, _>(uint_lt::).unwrap() 157 | } 158 | 159 | #[test] 160 | fn u64_lt() { 161 | run_binary_random::<1000, 64, _, _>(uint_lt::).unwrap() 162 | } 163 | 164 | #[test] 165 | fn u128_lt() { 166 | run_binary_random::<1000, 128, _, _>(uint_lt::).unwrap() 167 | } 168 | 169 | #[test] 170 | fn u8_le() { 171 | run_binary_exhaustive(uint_le::).unwrap() 172 | } 173 | 174 | #[test] 175 | fn u16_le() { 176 | run_binary_random::<1000, 16, _, _>(uint_le::).unwrap() 177 | } 178 | 179 | #[test] 180 | fn u32_le() { 181 | run_binary_random::<1000, 32, _, _>(uint_le::).unwrap() 182 | } 183 | 184 | #[test] 185 | fn u64_le() { 186 | run_binary_random::<1000, 64, _, _>(uint_le::).unwrap() 187 | } 188 | 189 | #[test] 190 | fn u128_le() { 191 | run_binary_random::<1000, 128, _, _>(uint_le::).unwrap() 192 | } 193 | 194 | #[test] 195 | fn u8_ge() { 196 | run_binary_exhaustive(uint_ge::).unwrap() 197 | } 198 | 199 | #[test] 200 | fn u16_ge() { 201 | run_binary_random::<1000, 16, _, _>(uint_ge::).unwrap() 202 | } 203 | 204 | #[test] 205 | fn u32_ge() { 206 | run_binary_random::<1000, 32, _, _>(uint_ge::).unwrap() 207 | } 208 | 209 | #[test] 210 | fn u64_ge() { 211 | run_binary_random::<1000, 64, _, _>(uint_ge::).unwrap() 212 | } 213 | 214 | #[test] 215 | fn u128_ge() { 216 | run_binary_random::<1000, 128, _, _>(uint_ge::).unwrap() 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/fields/emulated_fp/mod.rs: -------------------------------------------------------------------------------- 1 | //! ## Overview 2 | //! 3 | //! This module implements a field gadget for a prime field `Fp` over another 4 | //! prime field `Fq` where `p != q`. 5 | //! 6 | //! When writing constraint systems for many cryptographic proofs, we are 7 | //! restricted to a native field (e.g., the scalar field of the pairing-friendly 8 | //! curve). This can be inconvenient; for example, the recursive composition of 9 | //! proofs via cycles of curves requires the verifier to compute over a 10 | //! non-native field. 11 | //! 12 | //! The library makes it possible to write computations over a non-native field 13 | //! in the same way one would write computations over the native field. This 14 | //! naturally introduces additional overhead, which we minimize using a variety 15 | //! of optimizations. (Nevertheless, the overhead is still substantial, and 16 | //! native fields should be used where possible.) 17 | //! 18 | //! ## Usage 19 | //! 20 | //! Because [`EmulatedFpVar`] implements the [`FieldVar`] trait in arkworks, 21 | //! we can treat it like a native prime field variable ([`FpVar`]). 22 | //! 23 | //! We can do the standard field operations, such as `+`, `-`, and `*`. See the 24 | //! following example: 25 | //! 26 | //! ```rust 27 | //! # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 28 | //! # use ark_std::UniformRand; 29 | //! # use ark_relations::{ns, r1cs::ConstraintSystem}; 30 | //! # use ark_r1cs_std::prelude::*; 31 | //! use ark_r1cs_std::fields::emulated_fp::EmulatedFpVar; 32 | //! use ark_bls12_377::{Fr, Fq}; 33 | //! 34 | //! # let mut rng = ark_std::test_rng(); 35 | //! # let a_value = Fr::rand(&mut rng); 36 | //! # let b_value = Fr::rand(&mut rng); 37 | //! # let cs = ConstraintSystem::::new_ref(); 38 | //! 39 | //! let a = EmulatedFpVar::::new_witness(ns!(cs, "a"), || Ok(a_value))?; 40 | //! let b = EmulatedFpVar::::new_witness(ns!(cs, "b"), || Ok(b_value))?; 41 | //! 42 | //! // add 43 | //! let a_plus_b = &a + &b; 44 | //! 45 | //! // sub 46 | //! let a_minus_b = &a - &b; 47 | //! 48 | //! // multiply 49 | //! let a_times_b = &a * &b; 50 | //! 51 | //! // enforce equality 52 | //! a.enforce_equal(&b)?; 53 | //! # Ok(()) 54 | //! # } 55 | //! ``` 56 | //! 57 | //! ## Advanced optimization 58 | //! 59 | //! After each multiplication, our library internally performs a *reduce* 60 | //! operation, which reduces an intermediate type [`MulResultVar`] 61 | //! to the normalized type [`EmulatedFpVar`]. This enables a user to 62 | //! seamlessly perform a sequence of operations without worrying about the 63 | //! underlying details. 64 | //! 65 | //! However, this operation is expensive and is sometimes avoidable. We can 66 | //! reduce the number of constraints by using this intermediate type, which only 67 | //! supports additions. To multiply, it must be reduced back to 68 | //! [`EmulatedFpVar`]. See below for a skeleton example. 69 | //! 70 | //! --- 71 | //! 72 | //! To compute `a * b + c * d`, the straightforward (but more expensive) 73 | //! implementation is as follows: 74 | //! 75 | //! ```ignore 76 | //! let a_times_b = &a * &b; 77 | //! let c_times_d = &c * &d; 78 | //! let res = &a_times_b + &c_times_d; 79 | //! ``` 80 | //! 81 | //! This performs two *reduce* operations in total, one for each multiplication. 82 | //! 83 | //! --- 84 | //! 85 | //! We can save one reduction by using [`MulResultVar`], as 86 | //! follows: 87 | //! 88 | //! ```ignore 89 | //! let a_times_b = a.mul_without_reduce(&b)?; 90 | //! let c_times_d = c.mul_without_reduce(&d)?; 91 | //! let res = (&a_times_b + &c_times_d)?.reduce()?; 92 | //! ``` 93 | //! 94 | //! It performs only one *reduce* operation and is roughly 2x faster than the 95 | //! first implementation. 96 | //! 97 | //! ## Inspiration and basic design 98 | //! 99 | //! This implementation employs the standard idea of using multiple **limbs** to 100 | //! represent an element of the target field. For example, an element in the 101 | //! TargetF may be represented by three BaseF elements (i.e., the 102 | //! limbs). 103 | //! 104 | //! ```text 105 | //! TargetF -> limb 1, limb 2, and limb 3 (each is a BaseF element) 106 | //! ``` 107 | //! 108 | //! After some computation, the limbs become saturated and need to be 109 | //! **reduced**, in order to engage in more computation. 110 | //! 111 | //! We heavily use the optimization techniques in [\[KPS18\]](https://akosba.github.io/papers/xjsnark.pdf) and [\[OWWB20\]](https://eprint.iacr.org/2019/1494). 112 | //! Both works have their own open-source libraries: 113 | //! [xJsnark](https://github.com/akosba/xjsnark) and 114 | //! [bellman-bignat](https://github.com/alex-ozdemir/bellman-bignat). 115 | //! Compared with these, this module works with the `arkworks` ecosystem. 116 | //! It also provides the option (based on an `optimization_goal` for the 117 | //! constraint system) to optimize for constraint density instead of number of 118 | //! constraints, which improves efficiency in proof systems like [Marlin](https://github.com/arkworks-rs/marlin). 119 | //! 120 | //! ## References 121 | //! \[KPS18\]: A. E. Kosba, C. Papamanthou, and E. Shi. "xJsnark: a framework for efficient verifiable computation," in *Proceedings of the 39th Symposium on Security and Privacy*, ser. S&P ’18, 2018, pp. 944–961. 122 | //! 123 | //! \[OWWB20\]: A. Ozdemir, R. S. Wahby, B. Whitehat, and D. Boneh. "Scaling verifiable computation using efficient set accumulators," in *Proceedings of the 29th USENIX Security Symposium*, ser. Security ’20, 2020. 124 | //! 125 | //! [`EmulatedFpVar`]: crate::fields::emulated_fp::EmulatedFpVar 126 | //! [`MulResultVar`]: crate::fields::emulated_fp::MulResultVar 127 | //! [`FpVar`]: crate::fields::fp::FpVar 128 | 129 | #![allow( 130 | clippy::redundant_closure_call, 131 | clippy::enum_glob_use, 132 | clippy::missing_errors_doc, 133 | clippy::cast_possible_truncation, 134 | clippy::unseparated_literal_suffix 135 | )] 136 | 137 | use ark_std::fmt::Debug; 138 | 139 | /// Utilities for sampling parameters for non-native field gadgets 140 | /// 141 | /// - `BaseF`: the constraint field 142 | /// - `TargetF`: the field being simulated 143 | /// - `num_limbs`: how many limbs are used 144 | /// - `bits_per_limb`: the size of the limbs 145 | pub mod params; 146 | /// How are non-native elements reduced? 147 | pub(crate) mod reduce; 148 | 149 | /// a macro for computing ceil(log2(x)) for a field element x 150 | macro_rules! overhead { 151 | ($x:expr) => {{ 152 | use ark_ff::BigInteger; 153 | let num = $x; 154 | let num_bits = num.into_bigint().to_bits_be(); 155 | let mut skipped_bits = 0; 156 | for b in num_bits.iter() { 157 | if *b == false { 158 | skipped_bits += 1; 159 | } else { 160 | break; 161 | } 162 | } 163 | 164 | let mut is_power_of_2 = true; 165 | for b in num_bits.iter().skip(skipped_bits + 1) { 166 | if *b == true { 167 | is_power_of_2 = false; 168 | } 169 | } 170 | 171 | if is_power_of_2 { 172 | num_bits.len() - skipped_bits 173 | } else { 174 | num_bits.len() - skipped_bits + 1 175 | } 176 | }}; 177 | } 178 | 179 | pub(crate) use overhead; 180 | 181 | /// Parameters for a specific `EmulatedFpVar` instantiation 182 | #[derive(Clone, Debug)] 183 | pub struct NonNativeFieldConfig { 184 | /// The number of limbs (`BaseF` elements) used to represent a 185 | /// `TargetF` element. Highest limb first. 186 | pub num_limbs: usize, 187 | 188 | /// The number of bits of the limb 189 | pub bits_per_limb: usize, 190 | } 191 | 192 | mod allocated_field_var; 193 | pub use allocated_field_var::*; 194 | 195 | mod allocated_mul_result; 196 | pub use allocated_mul_result::*; 197 | 198 | mod field_var; 199 | pub use field_var::*; 200 | 201 | mod mul_result; 202 | pub use mul_result::*; 203 | -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::PrimeField; 2 | use ark_r1cs_std::{ 3 | alloc::AllocVar, 4 | eq::EqGadget, 5 | fields::{emulated_fp::EmulatedFpVar, FieldVar}, 6 | }; 7 | use ark_relations::{ 8 | ns, 9 | r1cs::{ConstraintSystem, ConstraintSystemRef, OptimizationGoal}, 10 | }; 11 | use ark_std::rand::RngCore; 12 | 13 | const NUM_REPETITIONS: usize = 1; 14 | 15 | fn get_density(cs: &ConstraintSystemRef) -> usize { 16 | match cs { 17 | ConstraintSystemRef::None => panic!("Constraint system is none."), 18 | ConstraintSystemRef::CS(r) => { 19 | let mut cs_bak = r.borrow().clone(); 20 | 21 | cs_bak.finalize(); 22 | let matrices = cs_bak.to_matrices().unwrap(); 23 | 24 | matrices.a_num_non_zero + matrices.b_num_non_zero + matrices.c_num_non_zero 25 | }, 26 | } 27 | } 28 | 29 | fn allocation( 30 | cs: ConstraintSystemRef, 31 | rng: &mut R, 32 | ) -> (usize, usize) { 33 | let a_native = TargetF::rand(rng); 34 | 35 | let constraints_before = cs.num_constraints(); 36 | let nonzeros_before = get_density(&cs); 37 | 38 | // There will be a check that ensures it has the reasonable number of bits 39 | let _ = EmulatedFpVar::::new_witness(ns!(cs, "alloc a"), || Ok(a_native)) 40 | .unwrap(); 41 | 42 | let constraints_after = cs.num_constraints(); 43 | let nonzeros_after = get_density(&cs); 44 | 45 | return ( 46 | constraints_after - constraints_before, 47 | nonzeros_after - nonzeros_before, 48 | ); 49 | } 50 | 51 | fn addition( 52 | cs: ConstraintSystemRef, 53 | rng: &mut R, 54 | ) -> (usize, usize) { 55 | let a_native = TargetF::rand(rng); 56 | let a = EmulatedFpVar::::new_witness(ns!(cs, "alloc a"), || Ok(a_native)) 57 | .unwrap(); 58 | 59 | let b_native = TargetF::rand(rng); 60 | let b = EmulatedFpVar::::new_witness(ns!(cs, "alloc b"), || Ok(b_native)) 61 | .unwrap(); 62 | 63 | let constraints_before = cs.num_constraints(); 64 | let nonzeros_before = get_density(&cs); 65 | 66 | let _ = &a + &b; 67 | 68 | let constraints_after = cs.num_constraints(); 69 | let nonzeros_after = get_density(&cs); 70 | 71 | return ( 72 | constraints_after - constraints_before, 73 | nonzeros_after - nonzeros_before, 74 | ); 75 | } 76 | 77 | fn equality( 78 | cs: ConstraintSystemRef, 79 | rng: &mut R, 80 | ) -> (usize, usize) { 81 | let a_native = TargetF::rand(rng); 82 | let a1 = EmulatedFpVar::::new_witness(ns!(cs, "alloc a1"), || Ok(a_native)) 83 | .unwrap(); 84 | let a2 = EmulatedFpVar::::new_witness(ns!(cs, "alloc a2"), || Ok(a_native)) 85 | .unwrap(); 86 | 87 | let constraints_before = cs.num_constraints(); 88 | let nonzeros_before = get_density(&cs); 89 | 90 | a1.enforce_equal(&a2).unwrap(); 91 | 92 | let constraints_after = cs.num_constraints(); 93 | let nonzeros_after = get_density(&cs); 94 | 95 | return ( 96 | constraints_after - constraints_before, 97 | nonzeros_after - nonzeros_before, 98 | ); 99 | } 100 | 101 | fn multiplication( 102 | cs: ConstraintSystemRef, 103 | rng: &mut R, 104 | ) -> (usize, usize) { 105 | let a_native = TargetF::rand(rng); 106 | let a = EmulatedFpVar::::new_witness(ns!(cs, "initial a"), || Ok(a_native)) 107 | .unwrap(); 108 | 109 | let b_native = TargetF::rand(rng); 110 | let b = EmulatedFpVar::::new_witness(ns!(cs, "initial b"), || Ok(b_native)) 111 | .unwrap(); 112 | 113 | let constraints_before = cs.num_constraints(); 114 | let nonzeros_before = get_density(&cs); 115 | 116 | let _ = &a * &b; 117 | 118 | let constraints_after = cs.num_constraints(); 119 | let nonzeros_after = get_density(&cs); 120 | 121 | return ( 122 | constraints_after - constraints_before, 123 | nonzeros_after - nonzeros_before, 124 | ); 125 | } 126 | 127 | fn inverse( 128 | cs: ConstraintSystemRef, 129 | rng: &mut R, 130 | ) -> (usize, usize) { 131 | let num_native = TargetF::rand(rng); 132 | let num = EmulatedFpVar::::new_witness(ns!(cs, "alloc"), || Ok(num_native)) 133 | .unwrap(); 134 | 135 | let constraints_before = cs.num_constraints(); 136 | let nonzeros_before = get_density(&cs); 137 | 138 | let _ = num.inverse().unwrap(); 139 | 140 | let constraints_after = cs.num_constraints(); 141 | let nonzeros_after = get_density(&cs); 142 | 143 | return ( 144 | constraints_after - constraints_before, 145 | nonzeros_after - nonzeros_before, 146 | ); 147 | } 148 | 149 | macro_rules! nonnative_bench_individual { 150 | ($bench_method:ident, $bench_name:ident, $bench_target_field:ty, $bench_base_field:ty) => { 151 | let rng = &mut ark_std::test_rng(); 152 | let mut num_constraints = 0; 153 | let mut num_nonzeros = 0; 154 | for _ in 0..NUM_REPETITIONS { 155 | let cs_sys = ConstraintSystem::<$bench_base_field>::new(); 156 | let cs = ConstraintSystemRef::new(cs_sys); 157 | cs.set_optimization_goal(OptimizationGoal::Constraints); 158 | 159 | let (cur_constraints, cur_nonzeros) = 160 | $bench_method::<$bench_target_field, $bench_base_field, _>(cs.clone(), rng); 161 | 162 | num_constraints += cur_constraints; 163 | num_nonzeros += cur_nonzeros; 164 | 165 | assert!(cs.is_satisfied().unwrap()); 166 | } 167 | let average_constraints = num_constraints / NUM_REPETITIONS; 168 | let average_nonzeros = num_nonzeros / NUM_REPETITIONS; 169 | println!( 170 | "{} takes: {} constraints, {} non-zeros", 171 | stringify!($bench_method), 172 | average_constraints, 173 | average_nonzeros, 174 | ); 175 | }; 176 | } 177 | 178 | macro_rules! nonnative_bench { 179 | ($bench_name:ident, $bench_target_field:ty, $bench_base_field:ty) => { 180 | println!( 181 | "For {} to simulate {}", 182 | stringify!($bench_base_field), 183 | stringify!($bench_target_field), 184 | ); 185 | nonnative_bench_individual!( 186 | allocation, 187 | $bench_name, 188 | $bench_target_field, 189 | $bench_base_field 190 | ); 191 | nonnative_bench_individual!( 192 | addition, 193 | $bench_name, 194 | $bench_target_field, 195 | $bench_base_field 196 | ); 197 | nonnative_bench_individual!( 198 | multiplication, 199 | $bench_name, 200 | $bench_target_field, 201 | $bench_base_field 202 | ); 203 | nonnative_bench_individual!( 204 | equality, 205 | $bench_name, 206 | $bench_target_field, 207 | $bench_base_field 208 | ); 209 | nonnative_bench_individual!(inverse, $bench_name, $bench_target_field, $bench_base_field); 210 | println!("----------------------") 211 | }; 212 | } 213 | 214 | fn main() { 215 | nonnative_bench!(MNT46Small, ark_mnt4_298::Fr, ark_mnt6_298::Fr); 216 | nonnative_bench!(MNT64Small, ark_mnt6_298::Fr, ark_mnt4_298::Fr); 217 | nonnative_bench!(MNT46Big, ark_mnt4_753::Fr, ark_mnt6_753::Fr); 218 | nonnative_bench!(MNT64Big, ark_mnt6_753::Fr, ark_mnt4_753::Fr); 219 | nonnative_bench!(BLS12MNT4Small, ark_bls12_381::Fr, ark_mnt4_298::Fr); 220 | nonnative_bench!(BLS12, ark_bls12_381::Fq, ark_bls12_381::Fr); 221 | nonnative_bench!(MNT6BigMNT4Small, ark_mnt6_753::Fr, ark_mnt4_298::Fr); 222 | } 223 | -------------------------------------------------------------------------------- /src/uint/rotate.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | impl UInt { 4 | /// Rotates `self` to the right by `by` steps, wrapping around. 5 | /// 6 | /// # Examples 7 | /// ``` 8 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 9 | /// // We'll use the BLS12-381 scalar field for our constraints. 10 | /// use ark_test_curves::bls12_381::Fr; 11 | /// use ark_relations::r1cs::*; 12 | /// use ark_r1cs_std::prelude::*; 13 | /// 14 | /// let cs = ConstraintSystem::::new_ref(); 15 | /// let a = UInt32::new_witness(cs.clone(), || Ok(0xb301u32))?; 16 | /// let b = UInt32::new_witness(cs.clone(), || Ok(0x10000b3))?; 17 | /// 18 | /// a.rotate_right(8).enforce_equal(&b)?; 19 | /// assert!(cs.is_satisfied().unwrap()); 20 | /// # Ok(()) 21 | /// # } 22 | /// ``` 23 | #[tracing::instrument(target = "r1cs", skip(self))] 24 | pub fn rotate_right(&self, by: usize) -> Self { 25 | let mut result = self.clone(); 26 | result.rotate_right_in_place(by); 27 | result 28 | } 29 | /// Rotates `self` to the right *in place* by `by` steps, wrapping around. 30 | /// 31 | /// # Examples 32 | /// ``` 33 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 34 | /// // We'll use the BLS12-381 scalar field for our constraints. 35 | /// use ark_test_curves::bls12_381::Fr; 36 | /// use ark_relations::r1cs::*; 37 | /// use ark_r1cs_std::prelude::*; 38 | /// 39 | /// let cs = ConstraintSystem::::new_ref(); 40 | /// let mut a = UInt32::new_witness(cs.clone(), || Ok(0xb301u32))?; 41 | /// let b = UInt32::new_witness(cs.clone(), || Ok(0x10000b3))?; 42 | /// 43 | /// a.rotate_right_in_place(8); 44 | /// a.enforce_equal(&b)?; 45 | /// assert!(cs.is_satisfied().unwrap()); 46 | /// # Ok(()) 47 | /// # } 48 | /// ``` 49 | #[tracing::instrument(target = "r1cs", skip(self))] 50 | pub fn rotate_right_in_place(&mut self, by: usize) { 51 | let by = by % N; 52 | // `[T]::rotate_left` corresponds to a `rotate_right` of the bits. 53 | self.bits.rotate_left(by); 54 | self.value = self.value.map(|v| v.rotate_right(by as u32)); 55 | } 56 | 57 | /// Rotates `self` to the left by `by` steps, wrapping around. 58 | /// 59 | /// # Examples 60 | /// ``` 61 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 62 | /// // We'll use the BLS12-381 scalar field for our constraints. 63 | /// use ark_test_curves::bls12_381::Fr; 64 | /// use ark_relations::r1cs::*; 65 | /// use ark_r1cs_std::prelude::*; 66 | /// 67 | /// let cs = ConstraintSystem::::new_ref(); 68 | /// let a = UInt32::new_witness(cs.clone(), || Ok(0x10000b3))?; 69 | /// let b = UInt32::new_witness(cs.clone(), || Ok(0xb301u32))?; 70 | /// 71 | /// a.rotate_left(8).enforce_equal(&b)?; 72 | /// assert!(cs.is_satisfied().unwrap()); 73 | /// # Ok(()) 74 | /// # } 75 | /// ``` 76 | #[tracing::instrument(target = "r1cs", skip(self))] 77 | pub fn rotate_left(&self, by: usize) -> Self { 78 | let mut result = self.clone(); 79 | result.rotate_left_in_place(by); 80 | result 81 | } 82 | 83 | /// Rotates `self` to the left *in place* by `by` steps, wrapping around. 84 | /// 85 | /// # Examples 86 | /// ``` 87 | /// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { 88 | /// // We'll use the BLS12-381 scalar field for our constraints. 89 | /// use ark_test_curves::bls12_381::Fr; 90 | /// use ark_relations::r1cs::*; 91 | /// use ark_r1cs_std::prelude::*; 92 | /// 93 | /// let cs = ConstraintSystem::::new_ref(); 94 | /// let mut a = UInt32::new_witness(cs.clone(), || Ok(0x10000b3))?; 95 | /// let b = UInt32::new_witness(cs.clone(), || Ok(0xb301u32))?; 96 | /// 97 | /// a.rotate_left_in_place(8); 98 | /// a.enforce_equal(&b)?; 99 | /// assert!(cs.is_satisfied().unwrap()); 100 | /// # Ok(()) 101 | /// # } 102 | /// ``` 103 | pub fn rotate_left_in_place(&mut self, by: usize) { 104 | let by = by % N; 105 | // `[T]::rotate_right` corresponds to a `rotate_left` of the bits. 106 | self.bits.rotate_right(by); 107 | self.value = self.value.map(|v| v.rotate_left(by as u32)); 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod tests { 113 | use super::*; 114 | use crate::{ 115 | alloc::{AllocVar, AllocationMode}, 116 | prelude::EqGadget, 117 | uint::test_utils::{run_unary_exhaustive, run_unary_random}, 118 | R1CSVar, 119 | }; 120 | use ark_ff::PrimeField; 121 | use ark_test_curves::bls12_381::Fr; 122 | 123 | fn uint_rotate_left( 124 | a: UInt, 125 | ) -> Result<(), SynthesisError> { 126 | let cs = a.cs(); 127 | let expected_mode = if a.is_constant() { 128 | AllocationMode::Constant 129 | } else { 130 | AllocationMode::Witness 131 | }; 132 | for shift in 0..N { 133 | let computed = a.rotate_left(shift); 134 | let expected = UInt::::new_variable( 135 | cs.clone(), 136 | || Ok(a.value()?.rotate_left(shift as u32)), 137 | expected_mode, 138 | )?; 139 | assert_eq!(expected.value(), computed.value()); 140 | expected.enforce_equal(&computed)?; 141 | if !a.is_constant() { 142 | assert!(cs.is_satisfied().unwrap()); 143 | } 144 | } 145 | Ok(()) 146 | } 147 | 148 | fn uint_rotate_right( 149 | a: UInt, 150 | ) -> Result<(), SynthesisError> { 151 | let cs = a.cs(); 152 | let expected_mode = if a.is_constant() { 153 | AllocationMode::Constant 154 | } else { 155 | AllocationMode::Witness 156 | }; 157 | for shift in 0..N { 158 | let computed = a.rotate_right(shift); 159 | let expected = UInt::::new_variable( 160 | cs.clone(), 161 | || Ok(a.value()?.rotate_right(shift as u32)), 162 | expected_mode, 163 | )?; 164 | assert_eq!(expected.value(), computed.value()); 165 | expected.enforce_equal(&computed)?; 166 | if !a.is_constant() { 167 | assert!(cs.is_satisfied().unwrap()); 168 | } 169 | } 170 | Ok(()) 171 | } 172 | 173 | #[test] 174 | fn u8_rotate_left() { 175 | run_unary_exhaustive(uint_rotate_left::).unwrap() 176 | } 177 | 178 | #[test] 179 | fn u16_rotate_left() { 180 | run_unary_random::<1000, 16, _, _>(uint_rotate_left::).unwrap() 181 | } 182 | 183 | #[test] 184 | fn u32_rotate_left() { 185 | run_unary_random::<1000, 32, _, _>(uint_rotate_left::).unwrap() 186 | } 187 | 188 | #[test] 189 | fn u64_rotate_left() { 190 | run_unary_random::<200, 64, _, _>(uint_rotate_left::).unwrap() 191 | } 192 | 193 | #[test] 194 | fn u128_rotate_left() { 195 | run_unary_random::<100, 128, _, _>(uint_rotate_left::).unwrap() 196 | } 197 | 198 | #[test] 199 | fn u8_rotate_right() { 200 | run_unary_exhaustive(uint_rotate_right::).unwrap() 201 | } 202 | 203 | #[test] 204 | fn u16_rotate_right() { 205 | run_unary_random::<1000, 16, _, _>(uint_rotate_right::).unwrap() 206 | } 207 | 208 | #[test] 209 | fn u32_rotate_right() { 210 | run_unary_random::<1000, 32, _, _>(uint_rotate_right::).unwrap() 211 | } 212 | 213 | #[test] 214 | fn u64_rotate_right() { 215 | run_unary_random::<200, 64, _, _>(uint_rotate_right::).unwrap() 216 | } 217 | 218 | #[test] 219 | fn u128_rotate_right() { 220 | run_unary_random::<100, 128, _, _>(uint_rotate_right::).unwrap() 221 | } 222 | } 223 | --------------------------------------------------------------------------------