lattice_commitments/
commitment.rsuse std::collections::HashMap;
use std::marker::PhantomData;
use rand::Rng;
use ring_math::Matrix2D;
use ring_math::Polynomial;
use ring_math::PolynomialRingElement;
use ring_math::Vector;
use scalarff::scalar_ring;
use scalarff::BigUint;
use scalarff::FieldElement;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug)]
pub struct Vcs<T: PolynomialRingElement> {
_phantom: PhantomData<T>,
pub k: usize, pub n: usize, pub l: usize, pub beta: usize, pub kappa: usize, pub theta: f64, pub N: usize, }
fn f64_to_u64(v: f64) -> u64 {
let z = v.ceil() as u64;
z
}
scalar_ring!(BetaRing, 2, "beta_bound_ring");
impl<T: PolynomialRingElement> Vcs<T> {
pub fn new(polynomial_degree: usize) -> Self {
let kappa: u32 = 36;
let beta: u32 = 1;
let k: u32 = 3;
let n: u32 = 1;
let l: u32 = 1;
if kappa > polynomial_degree as u32 {
panic!("kappa must be less than the polynomial degree otherwise challenge vector does not exist");
}
Vcs {
N: polynomial_degree,
_phantom: PhantomData,
k: usize::try_from(k).unwrap(),
n: usize::try_from(n).unwrap(),
l: usize::try_from(l).unwrap(),
beta: usize::try_from(beta).unwrap(),
kappa: usize::try_from(kappa).unwrap(),
theta: 11.0 * f64::from(kappa * beta) * f64::sqrt(f64::from(k) * 64.0),
}
}
pub fn sample_challenge_vector(&self) -> T {
let mut existing: HashMap<usize, bool> = HashMap::new();
let mut rng = rand::thread_rng();
while existing.len() < self.kappa {
let degree = rng.gen_range(0..self.N);
if existing.contains_key(°ree) {
continue;
}
existing.insert(degree, true);
}
let mut poly = T::zero().polynomial().clone();
for (degree, _) in existing.iter() {
poly.term(&T::F::one(), *degree);
}
T::from(poly)
}
#[cfg(feature = "zk")]
pub fn prove_opening(&self, alpha: Matrix2D<T>, r: Vector<T>) -> (Vector<T>, Vector<T>, T) {
let y = Vector::from_vec(
vec![T::zero(); self.k]
.iter()
.map(|_| {
T::from(u64::from(discrete_gaussian::sample_vartime(
self.theta,
&mut rand::thread_rng(),
)))
})
.collect::<Vec<_>>(),
);
let (alpha_1, _alpha_2) = self.decompose_alpha(alpha);
let t = alpha_1.clone() * y.clone();
let d = self.sample_challenge_vector();
let z = y + r * d.clone();
(t, z, d)
}
#[cfg(feature = "zk")]
pub fn verify_opening_proof(
&self,
t: Vector<T>,
d: T,
z: Vector<T>,
cm: Vector<T>,
alpha: Matrix2D<T>,
) -> bool {
for v in z.iter() {
if v.norm_l2() > BigUint::from(f64_to_u64(2.0 * self.theta * 8.0)) {
return false;
}
}
if d.norm_l1() != BigUint::from(u64::try_from(self.kappa).unwrap()) {
return false;
}
if d.norm_max() != T::one().to_biguint() {
return false;
}
let (alpha_1, _alpha_2) = self.decompose_alpha(alpha);
let (cm_1, _cm_2) = self.decompose_cm(cm);
let lhs = alpha_1 * z;
let rhs = t + cm_1 * d;
lhs == rhs
}
pub fn commit<R: rand::Rng>(
&self,
x: &Vector<T>,
rng: &mut R,
) -> (Matrix2D<T>, Vector<T>, Vector<T>) {
assert_eq!(self.l, x.len(), "invalid message length");
let r = Vector::from_vec(
vec![T::zero(); self.k]
.iter()
.map(|_| Self::sample_beta(rng))
.collect::<Vec<_>>(),
);
#[cfg(debug_assertions)]
{
for v in r.iter() {
assert!(v.norm_max() <= BetaRing::prime());
}
}
let (alpha_1, alpha_2) = self.public_params();
let alpha = alpha_1.compose_vertical(alpha_2.clone());
let inter1 = alpha.clone() * r.clone();
let inter2 = Vector::from_vec([vec![T::zero(); self.n], x.to_vec()].concat());
let commitment = inter2.clone() + inter1.clone();
(alpha, commitment, r)
}
pub fn open(
&self,
commitment: &Vector<T>,
alpha: &Matrix2D<T>,
x: &Vector<T>,
r: &Vector<T>,
) -> bool {
for v in r.iter() {
if v.norm_l2() > BigUint::from(f64_to_u64(4.0 * self.theta * 8.0)) {
return false;
}
}
let padded_x = Vector::from_vec([vec![T::zero(); self.n], x.to_vec()].concat());
let c = alpha.clone() * r.clone() + padded_x.clone();
c == *commitment
}
pub fn public_params(&self) -> (Matrix2D<T>, Matrix2D<T>) {
let alpha_1_prime =
Matrix2D::sample_uniform(self.n, self.k - self.n, &mut rand::thread_rng());
let alpha_2_prime =
Matrix2D::sample_uniform(self.l, self.k - self.n - self.l, &mut rand::thread_rng());
let alpha_1 = Matrix2D::identity(self.n).compose_horizontal(alpha_1_prime);
let alpha_2 = Matrix2D::zero(self.l, self.n)
.compose_horizontal(Matrix2D::identity(self.l))
.compose_horizontal(alpha_2_prime);
(alpha_1, alpha_2)
}
pub fn decompose_alpha(&self, alpha: Matrix2D<T>) -> (Matrix2D<T>, Matrix2D<T>) {
alpha.split_vertical(self.n, self.l)
}
pub fn decompose_cm(&self, cm: Vector<T>) -> (Vector<T>, Vector<T>) {
let v = cm.0.clone();
(Vector(v[..self.n].to_vec()), Vector(v[self.n..].to_vec()))
}
fn sample_beta<R: rand::Rng>(rng: &mut R) -> T {
T::from_polynomial(Polynomial {
coefficients: T::zero()
.coef()
.iter()
.map(|_| BetaRing::sample_uniform(rng).to_biguint())
.map(|v| T::F::from_biguint(&v))
.collect::<Vec<_>>(),
})
}
pub fn beta_bound() -> BigUint {
BetaRing::prime()
}
}