Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

algebra: poly: poly, authenticated-poly: Implement polynomial division methods #26

Merged
merged 3 commits into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 191 additions & 21 deletions src/algebra/poly/authenticated_poly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use std::{
cmp, iter,
ops::{Add, Mul, Neg, Sub},
ops::{Add, Div, Mul, Neg, Sub},
pin::Pin,
task::{Context, Poll},
};
Expand Down Expand Up @@ -288,39 +288,114 @@ impl<C: CurveGroup> Mul<&AuthenticatedDensePoly<C>> for &AuthenticatedDensePoly<
}
}

// --- Scalar Multiplication --- //

impl<C: CurveGroup> Mul<&Scalar<C>> for &AuthenticatedDensePoly<C> {
type Output = AuthenticatedDensePoly<C>;

fn mul(self, rhs: &Scalar<C>) -> Self::Output {
let new_coeffs = self.coeffs.iter().map(|coeff| coeff * rhs).collect_vec();
AuthenticatedDensePoly::from_coeffs(new_coeffs)
}
}
impl_borrow_variants!(AuthenticatedDensePoly<C>, Mul, mul, *, Scalar<C>, C: CurveGroup);
impl_commutative!(AuthenticatedDensePoly<C>, Mul, mul, *, Scalar<C>, C: CurveGroup);

impl<C: CurveGroup> Mul<&ScalarResult<C>> for &AuthenticatedDensePoly<C> {
type Output = AuthenticatedDensePoly<C>;

fn mul(self, rhs: &ScalarResult<C>) -> Self::Output {
let new_coeffs = self.coeffs.iter().map(|coeff| coeff * rhs).collect_vec();
AuthenticatedDensePoly::from_coeffs(new_coeffs)
}
}
impl_borrow_variants!(AuthenticatedDensePoly<C>, Mul, mul, *, ScalarResult<C>, C: CurveGroup);
impl_commutative!(AuthenticatedDensePoly<C>, Mul, mul, *, ScalarResult<C>, C: CurveGroup);

impl<C: CurveGroup> Mul<&AuthenticatedScalarResult<C>> for &AuthenticatedDensePoly<C> {
type Output = AuthenticatedDensePoly<C>;

fn mul(self, rhs: &AuthenticatedScalarResult<C>) -> Self::Output {
let new_coeffs = self.coeffs.iter().map(|coeff| coeff * rhs).collect_vec();
AuthenticatedDensePoly::from_coeffs(new_coeffs)
}
}
impl_borrow_variants!(AuthenticatedDensePoly<C>, Mul, mul, *, AuthenticatedScalarResult<C>, C: CurveGroup);
impl_commutative!(AuthenticatedDensePoly<C>, Mul, mul, *, AuthenticatedScalarResult<C>, C: CurveGroup);

// --- Division --- //
/// Given a public divisor b(x) and shared dividend a(x) = a_1(x) + a_2(x) for party shares a_1, a_2
/// We can divide each share locally to obtain a secret sharing of \floor{a(x) / b(x)}
///
/// To see this, consider that a_1(x) = q_1(x)b(x) + r_1(x) and a_2(x) = q_2(x)b(x) + r_2(x) where:
/// - deg(q_1) = deg(a_1) - deg(b)
/// - deg(q_2) = deg(a_2) - deg(b)
/// - deg(r_1) < deg(b)
/// - deg(r_2) < deg(b)
/// The floor division operator for a(x), b(x) returns q(x) such that there exists r(x): deg(r) < deg(b)
/// where a(x) = q(x)b(x) + r(x)
/// Note that a_1(x) + a_2(x) = (q_1(x) + q_2(x))b(x) + r_1(x) + r_2(x), where of course
/// deg(r_1 + r_2) < deg(b), so \floor{a(x) / b(x)} = q_1(x) + q_2(x); making q_1, q_2 additive
/// secret shares of the result as desired
impl<C: CurveGroup> Div<&DensePolynomialResult<C>> for &AuthenticatedDensePoly<C> {
joeykraut marked this conversation as resolved.
Show resolved Hide resolved
type Output = AuthenticatedDensePoly<C>;

fn div(self, rhs: &DensePolynomialResult<C>) -> Self::Output {
// We cannot break early if the remainder is exhausted because this will cause the gate
// sequencing to differ between parties in the MPC. Instead we execute the whole computation on
// both ends of the MPC
assert!(!rhs.coeffs.is_empty(), "cannot divide by zero polynomial");
let fabric = self.coeffs[0].fabric();

let quotient_degree = self.degree().saturating_sub(rhs.degree());
if quotient_degree == 0 {
return AuthenticatedDensePoly::zero(fabric);
}

let mut remainder = self.clone();
let mut quotient_coeffs = fabric.zeros_authenticated(quotient_degree + 1);

let divisor_leading_inverse = rhs.coeffs.last().unwrap().inverse();
for deg in (0..=quotient_degree).rev() {
joeykraut marked this conversation as resolved.
Show resolved Hide resolved
// Compute the quotient coefficient for this round
let remainder_leading_coeff = remainder.coeffs.last().unwrap();
let next_quotient_coeff = remainder_leading_coeff * &divisor_leading_inverse;

// Update the remainder and record the coefficient
for (i, divisor_coeff) in rhs.coeffs.iter().enumerate() {
let remainder_ind = deg + i;
remainder.coeffs[remainder_ind] =
&remainder.coeffs[remainder_ind] - divisor_coeff * &next_quotient_coeff;
}

quotient_coeffs[deg] = next_quotient_coeff;

// Pop the leading coefficient (now zero) from the remainder
remainder.coeffs.pop();
}

AuthenticatedDensePoly::from_coeffs(quotient_coeffs)
}
}
impl_borrow_variants!(AuthenticatedDensePoly<C>, Div, div, /, DensePolynomialResult<C>, C: CurveGroup);

#[cfg(test)]
mod test {
use ark_poly::{univariate::DensePolynomial, Polynomial};
use itertools::Itertools;
use ark_poly::Polynomial;
use rand::thread_rng;

use crate::{
algebra::{
poly_test_helpers::{random_poly, TestPolyField},
poly_test_helpers::{allocate_poly, random_poly, share_poly},
Scalar,
},
network::PartyId,
test_helpers::{execute_mock_mpc, TestCurve},
MpcFabric, PARTY0,
test_helpers::execute_mock_mpc,
PARTY0,
};

use super::AuthenticatedDensePoly;

/// The degree bound used for testing
const DEGREE_BOUND: usize = 100;

/// Allocate an authenticated polynomial in the given fabric
fn share_poly(
poly: DensePolynomial<TestPolyField>,
sender: PartyId,
fabric: &MpcFabric<TestCurve>,
) -> AuthenticatedDensePoly<TestCurve> {
let coeffs = poly.coeffs.iter().copied().map(Scalar::new).collect_vec();
let shared_coeffs = fabric.batch_share_scalar(coeffs, sender);

AuthenticatedDensePoly::from_coeffs(shared_coeffs)
}

/// Test evaluating a polynomial at a given point
#[tokio::test]
async fn test_eval() {
Expand Down Expand Up @@ -497,4 +572,99 @@ mod test {
assert!(res.is_ok());
assert_eq!(res.unwrap(), expected_res);
}

/// Tests multiplying by a public constant scalar
#[tokio::test]
async fn test_scalar_mul_constant() {
let mut rng = thread_rng();
let poly = random_poly(DEGREE_BOUND);
let scaling_factor = Scalar::random(&mut rng);

let expected_res = &poly * scaling_factor.inner();

let (res, _) = execute_mock_mpc(|fabric| {
let poly = poly.clone();
async move {
let shared_poly = share_poly(poly, PARTY0, &fabric);
(shared_poly * scaling_factor).open_authenticated().await
}
})
.await;

assert!(res.is_ok());
assert_eq!(res.unwrap(), expected_res);
}

/// Tests multiplying by a public result
#[tokio::test]
async fn test_scalar_mul_public() {
let mut rng = thread_rng();
let poly = random_poly(DEGREE_BOUND);
let scaling_factor = Scalar::random(&mut rng);

let expected_res = &poly * scaling_factor.inner();

let (res, _) = execute_mock_mpc(|fabric| {
let poly = poly.clone();
async move {
let shared_poly = share_poly(poly, PARTY0, &fabric);
let scaling_factor = fabric.allocate_scalar(scaling_factor);

(shared_poly * scaling_factor).open_authenticated().await
}
})
.await;

assert!(res.is_ok());
assert_eq!(res.unwrap(), expected_res);
}

/// Tests multiplying by a shared scalar
#[tokio::test]
async fn test_scalar_mul() {
let mut rng = thread_rng();
let poly = random_poly(DEGREE_BOUND);
let scaling_factor = Scalar::random(&mut rng);

let expected_res = &poly * scaling_factor.inner();

let (res, _) = execute_mock_mpc(|fabric| {
let poly = poly.clone();
async move {
let shared_poly = share_poly(poly, PARTY0, &fabric);
let scaling_factor = fabric.share_scalar(scaling_factor, PARTY0);

(shared_poly * scaling_factor).open_authenticated().await
}
})
.await;

assert!(res.is_ok());
assert_eq!(res.unwrap(), expected_res);
}

/// Tests dividing a shared polynomial by a public polynomial
#[tokio::test]
async fn test_div_polynomial_public() {
let poly1 = random_poly(DEGREE_BOUND);
let poly2 = random_poly(DEGREE_BOUND);

let expected_res = &poly1 / &poly2;

let (res, _) = execute_mock_mpc(|fabric| {
let poly1 = poly1.clone();
let poly2 = poly2.clone();
async move {
let dividend = share_poly(poly1, PARTY0, &fabric);
let divisor = allocate_poly(&poly2, &fabric);

let quotient = dividend / divisor;
quotient.open_authenticated().await
}
})
.await;

assert!(res.is_ok());
assert_eq!(res.unwrap(), expected_res);
}
}
32 changes: 30 additions & 2 deletions src/algebra/poly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ pub use poly::*;
#[cfg(test)]
pub mod poly_test_helpers {
use ark_ec::Group;
use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial};
use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial};
use ark_std::UniformRand;
use itertools::Itertools;
use rand::{thread_rng, Rng};

use crate::test_helpers::TestCurve;
use crate::{algebra::Scalar, network::PartyId, test_helpers::TestCurve, MpcFabric};

use super::{AuthenticatedDensePoly, DensePolynomialResult};

/// The scalar field testing polynomials are defined over
pub type TestPolyField = <TestCurve as Group>::ScalarField;
Expand All @@ -36,4 +39,29 @@ pub mod poly_test_helpers {

DensePolynomial::from_coefficients_vec(coeffs)
}

/// Allocate a polynomial in an MPC fabric
pub fn allocate_poly(
poly: &DensePolynomial<TestPolyField>,
fabric: &MpcFabric<TestCurve>,
) -> DensePolynomialResult<TestCurve> {
let mut allocated_coeffs = Vec::with_capacity(poly.degree() + 1);
for coeff in poly.coeffs().iter() {
allocated_coeffs.push(fabric.allocate_scalar(Scalar::new(*coeff)));
}

DensePolynomialResult::from_coeffs(allocated_coeffs)
}

/// Allocate an authenticated polynomial in the given fabric
pub fn share_poly(
poly: DensePolynomial<TestPolyField>,
sender: PartyId,
fabric: &MpcFabric<TestCurve>,
) -> AuthenticatedDensePoly<TestCurve> {
let coeffs = poly.coeffs.iter().copied().map(Scalar::new).collect_vec();
let shared_coeffs = fabric.batch_share_scalar(coeffs, sender);

AuthenticatedDensePoly::from_coeffs(shared_coeffs)
}
}
Loading
Loading