Add constraints on m (#9)

* feat: add constraint on `m`

* fix: `poly_distribution` and `poly_operations` take references to AssignedCell

* chore: update `README`
This commit is contained in:
enrico.eth
2023-11-08 10:27:21 +01:00
committed by GitHub
parent b676653d24
commit 4a400d77c6
6 changed files with 127 additions and 62 deletions

9
.gitignore vendored
View File

@@ -14,4 +14,11 @@ Cargo.lock
*.pdb
# Halo2 artifacts
params/
params/
# Proving and Verifying keys
/data/*.pk
/data/*.vk
# Snark Proof
/data/*.snark

View File

@@ -5,13 +5,46 @@ Implementation based on [Revisiting Homomorphic Encryption Schemes for Finite Fi
The application is not production ready and is only meant to be used for educational purposes.
`LOOKUP_BITS=8 cargo run --example bfv -- --name bfv -k 14 mock`
## Quick Start
The input data is located in the `data` folder. This test vector file can be generated using [rlwe-py](https://github.com/yuriko627/rlwe-py)
**Mock Prover**
### Chips
`LOOKUP_BITS=8 cargo run --example bfv -- --name bfv -k 9 --input bfv.in mock`
- `check_poly_from_distribution_chi_error` - Enforces polynomial to be sampled from the chi distribution
The `MockProver` does not run the cryptographic prover on your circuit, but instead directly checks if constraints are satisfied. This is useful for testing purposes, and runs faster than the actual prover.
- `LOOKUP_BITS`, in the backend build a lookup table filled with value in the range [0, 2**LOOKUP_BITS)
- `bfv` is the name of the circuit located in `examples/bfv.rs`
- `bfv.in` is the input file for the circuit located in `data/bfv.in`. This test vector file can be generated for different encryption using [bfv-py](https://github.com/yuriko627/bfv-py)
- `-k` is the DEGREE of the circuit as you specify to set the circuit to have `2^k` number of rows. The number of rows is determined by the number of constraints in the circuit. Working with larger data inputs will require a larger degree.
**Key Generation**
`LOOKUP_BITS=8 cargo run --example bfv -- --name bfv -k 9 --input bfv.in keygen`
To generate a random universal trusted setup (for testing only!) and the proving and verifying keys for your circuit.
For technical reasons (related to [axiom Halo2-scaffold](https://github.com/axiom-crypto/halo2-scaffold)), keygen still requires an input file of the correct format. You can use the same input file as for the prover. But be aware that the actual input data are not encoded in the key generation.
This will generate a proving key `data/bfv.pk` and a verifying key `data/bfv.vk`. It will also generate a file `configs/bfv.json` which describes (and pins down) the configuration of the circuit. This configuration file is later read by the prover.
**Proof Generation**
`LOOKUP_BITS=8 cargo run --example bfv -- --name bfv -k 9 --input bfv.in prove`
This creates a SNARK proof, stored as a binary file `data/bfv.snark`, using the inputs read (by default) from `data/halbfvo2_lib.in``. You can specify a different input file with the option `--input filename.in`, which would look for a file at `data/filename.in``.
Using the same proving key, you can generate proofs for the same ZK circuit on different inputs using this command.
**Proof Verification**
`LOOKUP_BITS=8 cargo run --example bfv -- --name bfv -k 9 verify`
Verify the proof generated above
## Chips
- `check_poly_coefficients_in_range` - Enforces polynomial coefficients to be within a specified range
- `check_poly_from_distribution_chi_key` - Enforces polynomial to be sampled from the chi key
- `poly_add` - Enforces polynomial addition
- `poly_mul_equal_deg` - Enforces polynomial multiplication between polynomials of equal degree
@@ -19,3 +52,4 @@ The input data is located in the `data` folder. This test vector file can be gen
- `poly_scalar_mul` - Enforces scalar multiplication of a polynomial
- `poly_reduce` - Enforces reduction of polynomial coefficients by a modulus
- `poly_divide_by_cyclo` - Enforces the reduction of a polynomial by a cyclotomic polynomial

22
configs/bfv.json Normal file
View File

@@ -0,0 +1,22 @@
{
"params": {
"degree": 9,
"num_advice": 8,
"num_lookup_advice": 2,
"num_fixed": 1,
"lookup_bits": 8
},
"break_points": [
[
501,
500,
501,
502,
502,
500,
501
],
[],
[]
]
}

View File

@@ -13,7 +13,7 @@ use halo2_scaffold::scaffold::cmd::Cli;
use halo2_scaffold::scaffold::run;
use serde::{Deserialize, Serialize};
use zk_fhe::chips::poly_distribution::{
check_poly_from_distribution_chi_error, check_poly_from_distribution_chi_key,
check_poly_coefficients_in_range, check_poly_from_distribution_chi_key,
};
use zk_fhe::chips::poly_operations::{
poly_add, poly_divide_by_cyclo, poly_mul_equal_deg, poly_reduce, poly_scalar_mul,
@@ -178,8 +178,8 @@ fn bfv_encryption_circuit<F: ScalarField>(
*/
// Assumption for the chip is that B < Q which is satisfied by circuit assumption
check_poly_from_distribution_chi_error::<{ DEG - 1 }, Q, B, F>(ctx, e0.clone(), &range);
check_poly_from_distribution_chi_error::<{ DEG - 1 }, Q, B, F>(ctx, e1.clone(), &range);
check_poly_coefficients_in_range::<{ DEG - 1 }, Q, B, F>(ctx, &e0, &range);
check_poly_coefficients_in_range::<{ DEG - 1 }, Q, B, F>(ctx, &e1, &range);
/* constraint on u
- u must be a polynomial in the R_q ring => Coefficients must be in the [0, Q-1] range and the degree of u must be DEG - 1
@@ -191,7 +191,7 @@ fn bfv_encryption_circuit<F: ScalarField>(
- The assignment for loop above guarantees that the degree of u is DEG - 1
*/
check_poly_from_distribution_chi_key::<{ DEG - 1 }, Q, F>(ctx, u.clone(), range.gate());
check_poly_from_distribution_chi_key::<{ DEG - 1 }, Q, F>(ctx, &u, range.gate());
/* constraint on m
- m must be a polynomial in the R_t ring => Coefficients must be in the [0, T/2] OR [Q - T/2, Q - 1] range and the degree of m must be DEG - 1
@@ -201,6 +201,8 @@ fn bfv_encryption_circuit<F: ScalarField>(
- The assignment for loop above guarantees that the degree of m is DEG - 1
*/
check_poly_coefficients_in_range::<{ DEG - 1 }, Q, { T / 2 }, F>(ctx, &m, &range);
// 1. COMPUTE C0 (c0 is the first ciphertext component)
// pk0 * u
@@ -224,7 +226,7 @@ fn bfv_encryption_circuit<F: ScalarField>(
// Q needs to be chosen such that (Q-1) * (Q-1) * DEG < p where p is the prime field of the circuit in order to avoid overflow during the polynomial multiplication.
// (Q-1) * (Q-1) * DEG < p according to the assumption of the circuit.
let pk0_u = poly_mul_equal_deg::<{ DEG - 1 }, F>(ctx, pk0.clone(), u.clone(), &range.gate());
let pk0_u = poly_mul_equal_deg::<{ DEG - 1 }, F>(ctx, &pk0, &u, &range.gate());
// pk0_u is a polynomial of degree (DEG - 1) * 2 = 2*DEG - 2
// pk0_u has coefficients in the [0, (Q-1) * (Q-1) * DEG] range
@@ -239,7 +241,7 @@ fn bfv_encryption_circuit<F: ScalarField>(
// The coefficients of pk0_u are in the range [0, (Q-1) * (Q-1) * DEG] according to the polynomial multiplication constraint set above.
// Therefore the coefficients of pk0_u are known to have <= `num_bits_1` bits, therefore they satisfy the assumption of the `poly_reduce` chip
let pk0_u = poly_reduce::<{ 2 * DEG - 2 }, Q, F>(ctx, pk0_u, &range, num_bits_1);
let pk0_u = poly_reduce::<{ 2 * DEG - 2 }, Q, F>(ctx, &pk0_u, &range, num_bits_1);
// pk0_u is a polynomial of degree (DEG - 1) * 2 = 2*DEG - 2
// pk0_u has coefficients in the [0, Q-1] range
@@ -253,8 +255,7 @@ fn bfv_encryption_circuit<F: ScalarField>(
// - The coefficients of dividend and divisor can be expressed as u64 values as long as Q - 1 is less than 2^64
// - Q is chosen such that (Q-1) * (2 * DEG - 2 - DEG + 1)] + Q-1 < p. Note that this is a subset of the condition (Q-1) * (Q-1) * DEG < p which is an assumption of the circuit.
let pk0_u =
poly_divide_by_cyclo::<{ 2 * DEG - 2 }, DEG, Q, F>(ctx, pk0_u, cyclo.clone(), &range);
let pk0_u = poly_divide_by_cyclo::<{ 2 * DEG - 2 }, DEG, Q, F>(ctx, &pk0_u, &cyclo, &range);
// assert that the degree of pk0_u is 2*DEG - 2
@@ -299,7 +300,7 @@ fn bfv_encryption_circuit<F: ScalarField>(
// Note that this condition is a subset of the condition (Q-1) * (Q-1) * DEG < p which is an assumption of the circuit.
let m_delta =
poly_scalar_mul::<{ DEG - 1 }, F>(ctx, m.clone(), Constant(F::from(DELTA)), range.gate());
poly_scalar_mul::<{ DEG - 1 }, F>(ctx, &m, &Constant(F::from(DELTA)), range.gate());
// m_delta is a polynomial of degree DEG - 1
// Coefficients of m_delta are in the [0, (Q-1) * (Q/T)] range
@@ -307,14 +308,14 @@ fn bfv_encryption_circuit<F: ScalarField>(
// Reduce the coefficients of `m_delta` by modulo `Q`
// get the number of bits needed to represent the value of (Q-1) * (Q/T)
let binary_representation = format!("{:b}", ((Q - 1) * (Q / T)));
let num_bits_2 = binary_representation.len();
// The coefficients of m_delta are in the range [0, (Q-1) * (Q/T)] according to the polynomial scalar multiplication constraint set above.
// Therefore the coefficients of m_delta are known to have <= `num_bits_2` bits, therefore they satisfy the assumption of the `poly_reduce` chip
let m_delta = poly_reduce::<{ DEG - 1 }, Q, F>(ctx, m_delta, &range, num_bits_2);
let m_delta = poly_reduce::<{ DEG - 1 }, Q, F>(ctx, &m_delta, &range, num_bits_2);
// Note: Scalar multiplication does not change the degree of the polynomial, therefore we do not need to reduce the coefficients by the cyclotomic polynomial of degree `DEG` => x^DEG + 1
// m_delta is a polynomial in the R_q ring
@@ -336,7 +337,7 @@ fn bfv_encryption_circuit<F: ScalarField>(
// Note that this condition is a subset of the condition (Q-1) * (Q-1) * DEG < p which is an assumption of the circuit.
let pk0_u_trimmed_plus_m_delta =
poly_add::<{ DEG - 1 }, F>(ctx, pk0_u_trimmed, m_delta, range.gate());
poly_add::<{ DEG - 1 }, F>(ctx, &pk0_u_trimmed, &m_delta, range.gate());
// Reduce the coefficients of `m_delta` by modulo `Q`
// Coefficients of pk0_u_trimmed_plus_m_delta are in the [0, 2Q - 2] range
@@ -352,7 +353,7 @@ fn bfv_encryption_circuit<F: ScalarField>(
// Reduce the coefficients of `pk0_u_trimmed_plus_m_delta` by modulo `Q`
let pk0_u_trimmed_plus_m_delta =
poly_reduce::<{ DEG - 1 }, Q, F>(ctx, pk0_u_trimmed_plus_m_delta, &range, num_bits_3);
poly_reduce::<{ DEG - 1 }, Q, F>(ctx, &pk0_u_trimmed_plus_m_delta, &range, num_bits_3);
// Note: Addition does not change the degree of the polynomial, therefore we do not need to reduce the coefficients by the cyclotomic polynomial of degree `DEG` => x^DEG + 1
// pk0_u_trimmed_plus_m_delta is a polynomial in the R_q ring
@@ -373,14 +374,14 @@ fn bfv_encryption_circuit<F: ScalarField>(
// If the condition (Q-1) + (Q-1) < p is satisfied there is no risk of overflow during the polynomial addition.
// Note that this condition is a subset of the condition (Q-1) * (Q-1) * DEG < p which is an assumption of the circuit.
let c0 = poly_add::<{ DEG - 1 }, F>(ctx, pk0_u_trimmed_plus_m_delta, e0, range.gate());
let c0 = poly_add::<{ DEG - 1 }, F>(ctx, &pk0_u_trimmed_plus_m_delta, &e0, range.gate());
// The coefficients of c0 are in the range [0, 2Q - 2] according to the polynomial addition constraint set above.
// Therefore the coefficients of c0 are known to have <= `num_bits_3` bits, therefore they satisfy the assumption of the `poly_reduce` chip
// Reduce the coefficients of `pk0_u_trimmed_plus_m_delta` by modulo `Q`
let c0 = poly_reduce::<{ DEG - 1 }, Q, F>(ctx, c0, &range, num_bits_3);
let c0 = poly_reduce::<{ DEG - 1 }, Q, F>(ctx, &c0, &range, num_bits_3);
// Note: Addition does not change the degree of the polynomial, therefore we do not need to reduce the coefficients by the cyclotomic polynomial of degree `DEG` => x^DEG + 1
// c0 is a polynomial in the R_q ring!
@@ -408,7 +409,7 @@ fn bfv_encryption_circuit<F: ScalarField>(
// Q needs to be chosen such that (Q-1) * (Q-1) * DEG < p where p is the prime field of the circuit in order to avoid overflow during the polynomial multiplication.
// (Q-1) * (Q-1) * DEG < p according to the assumption of the circuit.
let pk1_u = poly_mul_equal_deg::<{ DEG - 1 }, F>(ctx, pk1.clone(), u, range.gate());
let pk1_u = poly_mul_equal_deg::<{ DEG - 1 }, F>(ctx, &pk1, &u, range.gate());
// pk1_u is a polynomial of degree (DEG - 1) * 2 = 2*DEG - 2
// pk1_u has coefficients in the [0, (Q-1) * (Q-1) * DEG] range
@@ -418,7 +419,7 @@ fn bfv_encryption_circuit<F: ScalarField>(
// The coefficients of pk1_u are in the range [0, (Q-1) * (Q-1) * DEG] according to the polynomial multiplication constraint set above.
// Therefore the coefficients of pk1_u are known to have <= `num_bits_1` bits, therefore they satisfy the assumption of the `poly_reduce` chip
let pk1_u = poly_reduce::<{ 2 * DEG - 2 }, Q, F>(ctx, pk1_u, &range, num_bits_1);
let pk1_u = poly_reduce::<{ 2 * DEG - 2 }, Q, F>(ctx, &pk1_u, &range, num_bits_1);
// pk1_u is a polynomial of degree (DEG - 1) * 2 = 2*DEG - 2
// pk1_u has coefficients in the [0, Q-1] range
@@ -432,8 +433,7 @@ fn bfv_encryption_circuit<F: ScalarField>(
// - The coefficients of dividend and divisor can be expressed as u64 values as long as Q - 1 is less than 2^64
// - Q is chosen such that (Q-1) * (2 * DEG - 2 - DEG + 1)] + Q-1 < p. Note that this is a subset of the condition (Q-1) * (Q-1) * DEG < p which is an assumption of the circuit.
let pk1_u =
poly_divide_by_cyclo::<{ 2 * DEG - 2 }, DEG, Q, F>(ctx, pk1_u, cyclo.clone(), &range);
let pk1_u = poly_divide_by_cyclo::<{ 2 * DEG - 2 }, DEG, Q, F>(ctx, &pk1_u, &cyclo, &range);
// assert that the degree of pk1_u is 2*DEG - 2
@@ -479,14 +479,14 @@ fn bfv_encryption_circuit<F: ScalarField>(
// Perform the polynomial addition between pk1_u_trimmed and e1.
let c1 = poly_add::<{ DEG - 1 }, F>(ctx, pk1_u_trimmed, e1, range.gate());
let c1 = poly_add::<{ DEG - 1 }, F>(ctx, &pk1_u_trimmed, &e1, range.gate());
// The coefficients of c1 are in the range [0, 2Q - 2] according to the polynomial addition constraint set above.
// Therefore the coefficients of c1 are known to have <= `num_bits_3` bits, therefore they satisfy the assumption of the `poly_reduce` chip
// Reduce the coefficients of `c1` by modulo `Q`
let c1 = poly_reduce::<{ DEG - 1 }, Q, F>(ctx, c1, &range, num_bits_3);
let c1 = poly_reduce::<{ DEG - 1 }, Q, F>(ctx, &c1, &range, num_bits_3);
// Note: Addition does not change the degree of the polynomial, therefore we do not need to reduce the coefficients by the cyclotomic polynomial of degree `DEG` => x^DEG + 1
// c1 is a polynomial in the R_q ring

View File

@@ -7,29 +7,30 @@ use halo2_base::AssignedValue;
use halo2_base::Context;
use halo2_base::QuantumCell::Constant;
/// Enforce that polynomial a of degree DEG is sampled from the distribution chi error
/// Enforce that polynomial a of degree DEG has coefficients in the range [0, Z] or [Q-Z, Q-1]
///
/// * Namely, that the coefficients are in the range [0, B] OR [Q-B, Q-1]
/// * DEG is the degree of the polynomial
/// * Assumes that B < Q
pub fn check_poly_from_distribution_chi_error<
/// * Q is the modulus of the ring R_q (cipher text space)
/// * Z is the constant that defines the range
/// * Assumes that Z < Q
pub fn check_poly_coefficients_in_range<
const DEG: usize,
const Q: u64,
const B: u64,
const Z: u64,
F: ScalarField,
>(
ctx: &mut Context<F>,
a: Vec<AssignedValue<F>>,
a: &Vec<AssignedValue<F>>,
range: &RangeChip<F>,
) {
// assert that the degree of the polynomial a is equal to DEG
assert_eq!(a.len() - 1, DEG);
// The goal is to check that coeff is in the range [0, B] OR [Q-B, Q-1]
// The goal is to check that coeff is in the range [0, Z] OR [Q-Z, Q-1]
// We split this check into two checks:
// - Check that coeff is in the range [0, B] and store the boolean result in in_partial_range_1_vec
// - Check that coeff is in the range [Q-B, Q-1] and store the boolean result in in_partial_range_2_vec
// We then perform (`in_partial_range_1` OR `in_partial_range_2`) to check that coeff is in the range [0, B] OR [Q-B, Q-1]
// - Check that coeff is in the range [0, Z] and store the boolean result in in_partial_range_1_vec
// - Check that coeff is in the range [Q-Z, Q-1] and store the boolean result in in_partial_range_2_vec
// We then perform (`in_partial_range_1` OR `in_partial_range_2`) to check that coeff is in the range [0, Z] OR [Q-Z, Q-1]
// The result of this check is stored in the `in_range` vector.
// All the boolean values in `in_range` are then enforced to be true
let mut in_range_vec = Vec::with_capacity(DEG + 1);
@@ -38,23 +39,23 @@ pub fn check_poly_from_distribution_chi_error<
let binary_representation = format!("{:b}", Q);
let q_bits = binary_representation.len();
for coeff in &a {
for coeff in a {
// First of all, enforce that coefficient is in the [0, 2^q_bits] range
let bool = range.is_less_than_safe(ctx, *coeff, (1 << q_bits as u64) + 1);
range.gate().assert_is_const(ctx, &bool, &F::from(1));
// Check for the range [0, B]
// Check for the range [0, Z]
// coeff is known are known to have <= `q_bits` bits according to the constraint set above
// B + 1 is known to have <= `q_bits` bits according to assumption of the chip
// Z + 1 is known to have <= `q_bits` bits according to assumption of the chip
// Therefore it satisfies the assumption of `is_less_than` chip
let in_partial_range_1 = range.is_less_than(ctx, *coeff, Constant(F::from(B + 1)), q_bits);
let in_partial_range_1 = range.is_less_than(ctx, *coeff, Constant(F::from(Z + 1)), q_bits);
// Check for the range [Q-B, Q-1]
// Check for the range [Q-Z, Q-1]
// coeff is known are known to have <= `q_bits` bits according to the constraint set above
// Q - B is known to have <= `q_bits` bits according to assumption of the chip
// Q - Z is known to have <= `q_bits` bits according to assumption of the chip
// Therefore it satisfies the assumption of `is_less_than` chip
let not_in_range_lower_bound =
range.is_less_than(ctx, *coeff, Constant(F::from(Q - B)), q_bits);
range.is_less_than(ctx, *coeff, Constant(F::from(Q - Z)), q_bits);
let in_range_lower_bound = range.gate.not(ctx, not_in_range_lower_bound);
// coeff is known are known to have <= `q_bits` bits according to the constraint set above
@@ -65,7 +66,7 @@ pub fn check_poly_from_distribution_chi_error<
.gate
.and(ctx, in_range_lower_bound, in_range_upper_bound);
// Combined check for [0, b] OR [q-b, q-1]
// Combined check for [0, Z] OR [Q-Z, Q-1]
let in_range = range.gate.or(ctx, in_partial_range_1, in_partial_range_2);
in_range_vec.push(in_range);
}
@@ -81,9 +82,10 @@ pub fn check_poly_from_distribution_chi_error<
///
/// * Namely, that the coefficients are in the range [0, 1, Q-1].
/// * DEG is the degree of the polynomial
/// * Q is the modulus of the ring R_q (cipher text space)
pub fn check_poly_from_distribution_chi_key<const DEG: usize, const Q: u64, F: ScalarField>(
ctx: &mut Context<F>,
a: Vec<AssignedValue<F>>,
a: &Vec<AssignedValue<F>>,
gate: &GateChip<F>,
) {
// assert that the degree of the polynomial a is equal to DEG
@@ -94,7 +96,7 @@ pub fn check_poly_from_distribution_chi_key<const DEG: usize, const Q: u64, F: S
// (coeff - 0) * (coeff - 1) * (coeff - (q-1)) = 0
// loop over all the coefficients of the polynomial
for coeff in &a {
for coeff in a {
// constrain (a - 0)
let factor_1 = gate.sub(ctx, *coeff, Constant(F::from(0)));

View File

@@ -15,8 +15,8 @@ use halo2_base::QuantumCell;
/// * It assumes that the coefficients are constrained such to overflow during the polynomial addition
pub fn poly_add<const DEG: usize, F: ScalarField>(
ctx: &mut Context<F>,
a: Vec<AssignedValue<F>>,
b: Vec<AssignedValue<F>>,
a: &Vec<AssignedValue<F>>,
b: &Vec<AssignedValue<F>>,
gate: &GateChip<F>,
) -> Vec<AssignedValue<F>> {
// assert that the input polynomials have the same degree and this is equal to DEG
@@ -44,8 +44,8 @@ pub fn poly_add<const DEG: usize, F: ScalarField>(
/// * It assumes that the coefficients are constrained such to overflow during the polynomial multiplication
pub fn poly_mul_equal_deg<const DEG: usize, F: ScalarField>(
ctx: &mut Context<F>,
a: Vec<AssignedValue<F>>,
b: Vec<AssignedValue<F>>,
a: &Vec<AssignedValue<F>>,
b: &Vec<AssignedValue<F>>,
gate: &GateChip<F>,
) -> Vec<AssignedValue<F>> {
// assert that the input polynomials have the same degree and this is equal to DEG
@@ -91,8 +91,8 @@ pub fn poly_mul_equal_deg<const DEG: usize, F: ScalarField>(
/// * It assumes that the coefficients are constrained such to overflow during the polynomial multiplication
pub fn poly_mul_diff_deg<F: ScalarField>(
ctx: &mut Context<F>,
a: Vec<AssignedValue<F>>,
b: Vec<AssignedValue<F>>,
a: &Vec<AssignedValue<F>>,
b: &Vec<AssignedValue<F>>,
gate: &GateChip<F>,
) -> Vec<AssignedValue<F>> {
let a_deg = a.len() - 1;
@@ -134,8 +134,8 @@ pub fn poly_mul_diff_deg<F: ScalarField>(
/// * It assumes that the coefficients are constrained such to overflow during the scalar multiplication
pub fn poly_scalar_mul<const DEG: usize, F: ScalarField>(
ctx: &mut Context<F>,
a: Vec<AssignedValue<F>>,
b: QuantumCell<F>,
a: &Vec<AssignedValue<F>>,
b: &QuantumCell<F>,
gate: &GateChip<F>,
) -> Vec<AssignedValue<F>> {
// assert that the degree of the polynomial a is equal to DEG
@@ -144,7 +144,7 @@ pub fn poly_scalar_mul<const DEG: usize, F: ScalarField>(
let mut c = vec![];
for i in 0..=DEG {
let val = gate.mul(ctx, a[i], b);
let val = gate.mul(ctx, a[i], *b);
c.push(val);
}
@@ -161,7 +161,7 @@ pub fn poly_scalar_mul<const DEG: usize, F: ScalarField>(
/// * It assumes that the coefficients of the input polynomial can be expressed in at most num_bits bits
pub fn poly_reduce<const DEG: usize, const Q: u64, F: ScalarField>(
ctx: &mut Context<F>,
input: Vec<AssignedValue<F>>,
input: &Vec<AssignedValue<F>>,
range: &RangeChip<F>,
num_bits: usize,
) -> Vec<AssignedValue<F>> {
@@ -188,7 +188,7 @@ pub fn poly_reduce<const DEG: usize, const Q: u64, F: ScalarField>(
///
/// * DEG_DVD is the degree of the `dividend` polynomial
/// * DEG_DVS is the degree of the `divisor` polynomial
/// * Q is the modulus of the Ring
/// * Q is the modulus of the ring R_q (cipher text space)
/// * Input polynomials is parsed as a vector of assigned coefficients [a_DEG, a_DEG-1, ..., a_1, a_0] where a_0 is the constant term
/// * Assumes that the degree of dividend is equal to (2 * DEG_DVS) - 2
/// * Assumes that the coefficients of `dividend` are in the range [0, Q - 1]
@@ -202,8 +202,8 @@ pub fn poly_divide_by_cyclo<
F: ScalarField,
>(
ctx: &mut Context<F>,
dividend: Vec<AssignedValue<F>>,
divisor: Vec<AssignedValue<F>>,
dividend: &Vec<AssignedValue<F>>,
divisor: &Vec<AssignedValue<F>>,
range: &RangeChip<F>,
) -> Vec<AssignedValue<F>> {
// Assert that degree of dividend polynomial is equal to the constant DEG_DVD
@@ -320,7 +320,7 @@ pub fn poly_divide_by_cyclo<
// We use a polynomial multiplication algorithm that does not require the input polynomials to be of the same degree
let prod = poly_mul_diff_deg(ctx, quotient, divisor, range.gate());
let prod = poly_mul_diff_deg(ctx, &quotient, divisor, range.gate());
// The degree of prod is DEG_DVD
assert_eq!(prod.len() - 1, DEG_DVD);
@@ -339,7 +339,7 @@ pub fn poly_divide_by_cyclo<
// Q needs to be chosen such that (Q-1) * (DEG_DVD - DEG_DVS + 1)] + Q-1 < p where p is the prime field of the circuit in order to avoid overflow during the addition.
// This is true by assumption of the chip.
let sum = poly_add::<DEG_DVD, F>(ctx, prod, remainder.clone(), range.gate());
let sum = poly_add::<DEG_DVD, F>(ctx, &prod, &remainder, range.gate());
// assert that the degree of sum is DEG_DVD
assert_eq!(sum.len() - 1, DEG_DVD);
@@ -355,7 +355,7 @@ pub fn poly_divide_by_cyclo<
// The coefficients of sum are in the range [0, (Q-1) * (DEG_DVD - DEG_DVS + 1)] + Q-1] according to the polynomial addition constraint set above.
// Therefore the coefficients of sum are known to have <= `num_bits` bits, therefore they satisfy the assumption of the `poly_reduce` chip
let sum_mod = poly_reduce::<DEG_DVD, Q, F>(ctx, sum, range, num_bits);
let sum_mod = poly_reduce::<DEG_DVD, Q, F>(ctx, &sum, range, num_bits);
// assert that the degree of sum_mod is DEG_DVD
assert_eq!(sum_mod.len() - 1, DEG_DVD);