#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![doc = include_str!("../README.md")] #![deny(missing_docs)] #![allow(non_snake_case)] use core::fmt; use std::collections::HashSet; use zeroize::Zeroize; use multiexp::{multiexp, multiexp_vartime}; use ciphersuite::{ group::{ff::Field, Group, GroupEncoding}, Ciphersuite, }; mod scalar_vector; pub use scalar_vector::ScalarVector; mod point_vector; pub use point_vector::PointVector; /// The transcript formats. pub mod transcript; pub(crate) mod inner_product; pub(crate) mod lincomb; /// The arithmetic circuit proof. pub mod arithmetic_circuit_proof; /// Functionlity useful when testing. #[cfg(any(test, feature = "tests"))] pub mod tests; /// Calculate the nearest power of two greater than or equivalent to the argument. pub(crate) fn padded_pow_of_2(i: usize) -> usize { let mut next_pow_of_2 = 1; while next_pow_of_2 < i { next_pow_of_2 <<= 1; } next_pow_of_2 } /// An error from working with generators. #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum GeneratorsError { /// The provided list of generators for `g` (bold) was empty. GBoldEmpty, /// The provided list of generators for `h` (bold) did not match `g` (bold) in length. DifferingGhBoldLengths, /// The amount of provided generators were not a power of two. NotPowerOfTwo, /// A generator was used multiple times. DuplicatedGenerator, } /// A full set of generators. #[derive(Clone)] pub struct Generators { g: C::G, h: C::G, g_bold: Vec, h_bold: Vec, h_sum: Vec, } /// A batch verifier of proofs. #[must_use] #[derive(Clone)] pub struct BatchVerifier { g: C::F, h: C::F, g_bold: Vec, h_bold: Vec, h_sum: Vec, additional: Vec<(C::F, C::G)>, } impl fmt::Debug for Generators { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let g = self.g.to_bytes(); let g: &[u8] = g.as_ref(); let h = self.h.to_bytes(); let h: &[u8] = h.as_ref(); fmt.debug_struct("Generators").field("g", &g).field("h", &h).finish_non_exhaustive() } } /// The generators for a specific proof. /// /// This potentially have been reduced in size from the original set of generators, as beneficial /// to performance. #[derive(Copy, Clone)] pub struct ProofGenerators<'a, C: Ciphersuite> { g: &'a C::G, h: &'a C::G, g_bold: &'a [C::G], h_bold: &'a [C::G], } impl fmt::Debug for ProofGenerators<'_, C> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let g = self.g.to_bytes(); let g: &[u8] = g.as_ref(); let h = self.h.to_bytes(); let h: &[u8] = h.as_ref(); fmt.debug_struct("ProofGenerators").field("g", &g).field("h", &h).finish_non_exhaustive() } } impl Generators { /// Construct an instance of Generators for usage with Bulletproofs. pub fn new( g: C::G, h: C::G, g_bold: Vec, h_bold: Vec, ) -> Result { if g_bold.is_empty() { Err(GeneratorsError::GBoldEmpty)?; } if g_bold.len() != h_bold.len() { Err(GeneratorsError::DifferingGhBoldLengths)?; } if padded_pow_of_2(g_bold.len()) != g_bold.len() { Err(GeneratorsError::NotPowerOfTwo)?; } let mut set = HashSet::new(); let mut add_generator = |generator: &C::G| { assert!(!bool::from(generator.is_identity())); let bytes = generator.to_bytes(); !set.insert(bytes.as_ref().to_vec()) }; assert!(!add_generator(&g), "g was prior present in empty set"); if add_generator(&h) { Err(GeneratorsError::DuplicatedGenerator)?; } for g in &g_bold { if add_generator(g) { Err(GeneratorsError::DuplicatedGenerator)?; } } for h in &h_bold { if add_generator(h) { Err(GeneratorsError::DuplicatedGenerator)?; } } let mut running_h_sum = C::G::identity(); let mut h_sum = vec![]; let mut next_pow_of_2 = 1; for (i, h) in h_bold.iter().enumerate() { running_h_sum += h; if (i + 1) == next_pow_of_2 { h_sum.push(running_h_sum); next_pow_of_2 *= 2; } } Ok(Generators { g, h, g_bold, h_bold, h_sum }) } /// Create a BatchVerifier for proofs which use these generators. pub fn batch_verifier(&self) -> BatchVerifier { BatchVerifier { g: C::F::ZERO, h: C::F::ZERO, g_bold: vec![C::F::ZERO; self.g_bold.len()], h_bold: vec![C::F::ZERO; self.h_bold.len()], h_sum: vec![C::F::ZERO; self.h_sum.len()], additional: Vec::with_capacity(128), } } /// Verify all proofs queued for batch verification in this BatchVerifier. #[must_use] pub fn verify(&self, verifier: BatchVerifier) -> bool { multiexp_vartime( &[(verifier.g, self.g), (verifier.h, self.h)] .into_iter() .chain(verifier.g_bold.into_iter().zip(self.g_bold.iter().cloned())) .chain(verifier.h_bold.into_iter().zip(self.h_bold.iter().cloned())) .chain(verifier.h_sum.into_iter().zip(self.h_sum.iter().cloned())) .chain(verifier.additional) .collect::>(), ) .is_identity() .into() } /// The `g` generator. pub fn g(&self) -> C::G { self.g } /// The `h` generator. pub fn h(&self) -> C::G { self.h } /// A slice to view the `g` (bold) generators. pub fn g_bold_slice(&self) -> &[C::G] { &self.g_bold } /// A slice to view the `h` (bold) generators. pub fn h_bold_slice(&self) -> &[C::G] { &self.h_bold } /// Reduce a set of generators to the quantity necessary to support a certain amount of /// in-circuit multiplications/terms in a Pedersen vector commitment. /// /// Returns None if reducing to 0 or if the generators reduced are insufficient to provide this /// many generators. pub fn reduce(&self, generators: usize) -> Option> { if generators == 0 { None?; } // Round to the nearest power of 2 let generators = padded_pow_of_2(generators); if generators > self.g_bold.len() { None?; } Some(ProofGenerators { g: &self.g, h: &self.h, g_bold: &self.g_bold[.. generators], h_bold: &self.h_bold[.. generators], }) } } impl<'a, C: Ciphersuite> ProofGenerators<'a, C> { pub(crate) fn len(&self) -> usize { self.g_bold.len() } pub(crate) fn g(&self) -> C::G { *self.g } pub(crate) fn h(&self) -> C::G { *self.h } pub(crate) fn g_bold(&self, i: usize) -> C::G { self.g_bold[i] } pub(crate) fn h_bold(&self, i: usize) -> C::G { self.h_bold[i] } pub(crate) fn g_bold_slice(&self) -> &[C::G] { self.g_bold } pub(crate) fn h_bold_slice(&self) -> &[C::G] { self.h_bold } } /// The opening of a Pedersen commitment. #[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)] pub struct PedersenCommitment { /// The value committed to. pub value: C::F, /// The mask blinding the value committed to. pub mask: C::F, } impl PedersenCommitment { /// Commit to this value, yielding the Pedersen commitment. pub fn commit(&self, g: C::G, h: C::G) -> C::G { multiexp(&[(self.value, g), (self.mask, h)]) } } /// The opening of a Pedersen vector commitment. #[derive(Clone, PartialEq, Eq, Debug, Zeroize)] pub struct PedersenVectorCommitment { /// The values committed to across the `g` (bold) generators. pub g_values: ScalarVector, /// The values committed to across the `h` (bold) generators. pub h_values: ScalarVector, /// The mask blinding the values committed to. pub mask: C::F, } impl PedersenVectorCommitment { /// Commit to the vectors of values. /// /// This function returns None if the amount of generators is less than the amount of values /// within the relevant vector. pub fn commit(&self, g_bold: &[C::G], h_bold: &[C::G], h: C::G) -> Option { if (g_bold.len() < self.g_values.len()) || (h_bold.len() < self.h_values.len()) { None?; }; let mut terms = vec![(self.mask, h)]; for pair in self.g_values.0.iter().cloned().zip(g_bold.iter().cloned()) { terms.push(pair); } for pair in self.h_values.0.iter().cloned().zip(h_bold.iter().cloned()) { terms.push(pair); } let res = multiexp(&terms); terms.zeroize(); Some(res) } }