//! This module provides an implementation of a commitment engine use crate::{ errors::NovaError, traits::{ commitment::{CommitmentEngineTrait, CommitmentTrait}, AbsorbInROTrait, CompressedGroup, Group, ROTrait, TranscriptReprTrait, }, }; use core::{ fmt::Debug, marker::PhantomData, ops::{Add, AddAssign, Mul, MulAssign}, }; use ff::Field; use rayon::prelude::*; use serde::{Deserialize, Serialize}; /// A type that holds commitment generators #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct CommitmentKey { ck: Vec, _p: PhantomData, } /// A type that holds a commitment #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(bound = "")] pub struct Commitment { pub(crate) comm: G, } /// A type that holds a compressed commitment #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(bound = "")] pub struct CompressedCommitment { comm: G::CompressedGroupElement, } impl CommitmentTrait for Commitment { type CompressedCommitment = CompressedCommitment; fn compress(&self) -> Self::CompressedCommitment { CompressedCommitment { comm: self.comm.compress(), } } fn to_coordinates(&self) -> (G::Base, G::Base, bool) { self.comm.to_coordinates() } fn decompress(c: &Self::CompressedCommitment) -> Result { let comm = c.comm.decompress(); if comm.is_none() { return Err(NovaError::DecompressionError); } Ok(Commitment { comm: comm.unwrap(), }) } } impl Default for Commitment { fn default() -> Self { Commitment { comm: G::zero() } } } impl TranscriptReprTrait for Commitment { fn to_transcript_bytes(&self) -> Vec { let (x, y, is_infinity) = self.comm.to_coordinates(); let is_infinity_byte = if is_infinity { 0u8 } else { 1u8 }; [ x.to_transcript_bytes(), y.to_transcript_bytes(), [is_infinity_byte].to_vec(), ] .concat() } } impl AbsorbInROTrait for Commitment { fn absorb_in_ro(&self, ro: &mut G::RO) { let (x, y, is_infinity) = self.comm.to_coordinates(); ro.absorb(x); ro.absorb(y); ro.absorb(if is_infinity { G::Base::ONE } else { G::Base::ZERO }); } } impl TranscriptReprTrait for CompressedCommitment { fn to_transcript_bytes(&self) -> Vec { self.comm.to_transcript_bytes() } } impl MulAssign for Commitment { fn mul_assign(&mut self, scalar: G::Scalar) { let result = (self as &Commitment).comm * scalar; *self = Commitment { comm: result }; } } impl<'a, 'b, G: Group> Mul<&'b G::Scalar> for &'a Commitment { type Output = Commitment; fn mul(self, scalar: &'b G::Scalar) -> Commitment { Commitment { comm: self.comm * scalar, } } } impl Mul for Commitment { type Output = Commitment; fn mul(self, scalar: G::Scalar) -> Commitment { Commitment { comm: self.comm * scalar, } } } impl<'b, G: Group> AddAssign<&'b Commitment> for Commitment { fn add_assign(&mut self, other: &'b Commitment) { let result = (self as &Commitment).comm + other.comm; *self = Commitment { comm: result }; } } impl<'a, 'b, G: Group> Add<&'b Commitment> for &'a Commitment { type Output = Commitment; fn add(self, other: &'b Commitment) -> Commitment { Commitment { comm: self.comm + other.comm, } } } macro_rules! define_add_variants { (G = $g:path, LHS = $lhs:ty, RHS = $rhs:ty, Output = $out:ty) => { impl<'b, G: $g> Add<&'b $rhs> for $lhs { type Output = $out; fn add(self, rhs: &'b $rhs) -> $out { &self + rhs } } impl<'a, G: $g> Add<$rhs> for &'a $lhs { type Output = $out; fn add(self, rhs: $rhs) -> $out { self + &rhs } } impl Add<$rhs> for $lhs { type Output = $out; fn add(self, rhs: $rhs) -> $out { &self + &rhs } } }; } macro_rules! define_add_assign_variants { (G = $g:path, LHS = $lhs:ty, RHS = $rhs:ty) => { impl AddAssign<$rhs> for $lhs { fn add_assign(&mut self, rhs: $rhs) { *self += &rhs; } } }; } define_add_assign_variants!(G = Group, LHS = Commitment, RHS = Commitment); define_add_variants!(G = Group, LHS = Commitment, RHS = Commitment, Output = Commitment); /// Provides a commitment engine #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct CommitmentEngine { _p: PhantomData, } impl CommitmentEngineTrait for CommitmentEngine { type CommitmentKey = CommitmentKey; type Commitment = Commitment; fn setup(label: &'static [u8], n: usize) -> Self::CommitmentKey { Self::CommitmentKey { ck: G::from_label(label, n.next_power_of_two()), _p: Default::default(), } } fn commit(ck: &Self::CommitmentKey, v: &[G::Scalar]) -> Self::Commitment { assert!(ck.ck.len() >= v.len()); Commitment { comm: G::vartime_multiscalar_mul(v, &ck.ck[..v.len()]), } } } pub(crate) trait CommitmentKeyExtTrait { type CE: CommitmentEngineTrait; /// Splits the commitment key into two pieces at a specified point fn split_at(&self, n: usize) -> (Self, Self) where Self: Sized; /// Combines two commitment keys into one fn combine(&self, other: &Self) -> Self; /// Folds the two commitment keys into one using the provided weights fn fold(&self, w1: &G::Scalar, w2: &G::Scalar) -> Self; /// Scales the commitment key using the provided scalar fn scale(&self, r: &G::Scalar) -> Self; /// Reinterprets commitments as commitment keys fn reinterpret_commitments_as_ck( c: &[<<>::CE as CommitmentEngineTrait>::Commitment as CommitmentTrait>::CompressedCommitment], ) -> Result where Self: Sized; } impl CommitmentKeyExtTrait for CommitmentKey { type CE = CommitmentEngine; fn split_at(&self, n: usize) -> (CommitmentKey, CommitmentKey) { ( CommitmentKey { ck: self.ck[0..n].to_vec(), _p: Default::default(), }, CommitmentKey { ck: self.ck[n..].to_vec(), _p: Default::default(), }, ) } fn combine(&self, other: &CommitmentKey) -> CommitmentKey { let ck = { let mut c = self.ck.clone(); c.extend(other.ck.clone()); c }; CommitmentKey { ck, _p: Default::default(), } } // combines the left and right halves of `self` using `w1` and `w2` as the weights fn fold(&self, w1: &G::Scalar, w2: &G::Scalar) -> CommitmentKey { let w = vec![*w1, *w2]; let (L, R) = self.split_at(self.ck.len() / 2); let ck = (0..self.ck.len() / 2) .into_par_iter() .map(|i| { let bases = [L.ck[i].clone(), R.ck[i].clone()].to_vec(); G::vartime_multiscalar_mul(&w, &bases).preprocessed() }) .collect(); CommitmentKey { ck, _p: Default::default(), } } /// Scales each element in `self` by `r` fn scale(&self, r: &G::Scalar) -> Self { let ck_scaled = self .ck .clone() .into_par_iter() .map(|g| G::vartime_multiscalar_mul(&[*r], &[g]).preprocessed()) .collect(); CommitmentKey { ck: ck_scaled, _p: Default::default(), } } /// reinterprets a vector of commitments as a set of generators fn reinterpret_commitments_as_ck(c: &[CompressedCommitment]) -> Result { let d = (0..c.len()) .into_par_iter() .map(|i| Commitment::::decompress(&c[i])) .collect::>, NovaError>>()?; let ck = (0..d.len()) .into_par_iter() .map(|i| d[i].comm.preprocessed()) .collect(); Ok(CommitmentKey { ck, _p: Default::default(), }) } }