mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-09 20:59:23 +00:00
329 lines
8.3 KiB
Rust
329 lines
8.3 KiB
Rust
|
|
#![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<C: Ciphersuite> {
|
||
|
|
g: C::G,
|
||
|
|
h: C::G,
|
||
|
|
|
||
|
|
g_bold: Vec<C::G>,
|
||
|
|
h_bold: Vec<C::G>,
|
||
|
|
h_sum: Vec<C::G>,
|
||
|
|
}
|
||
|
|
|
||
|
|
/// A batch verifier of proofs.
|
||
|
|
#[must_use]
|
||
|
|
#[derive(Clone)]
|
||
|
|
pub struct BatchVerifier<C: Ciphersuite> {
|
||
|
|
g: C::F,
|
||
|
|
h: C::F,
|
||
|
|
|
||
|
|
g_bold: Vec<C::F>,
|
||
|
|
h_bold: Vec<C::F>,
|
||
|
|
h_sum: Vec<C::F>,
|
||
|
|
|
||
|
|
additional: Vec<(C::F, C::G)>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl<C: Ciphersuite> fmt::Debug for Generators<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("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<C: Ciphersuite> 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<C: Ciphersuite> Generators<C> {
|
||
|
|
/// Construct an instance of Generators for usage with Bulletproofs.
|
||
|
|
pub fn new(
|
||
|
|
g: C::G,
|
||
|
|
h: C::G,
|
||
|
|
g_bold: Vec<C::G>,
|
||
|
|
h_bold: Vec<C::G>,
|
||
|
|
) -> Result<Self, GeneratorsError> {
|
||
|
|
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<C> {
|
||
|
|
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<C>) -> 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::<Vec<_>>(),
|
||
|
|
)
|
||
|
|
.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<ProofGenerators<'_, C>> {
|
||
|
|
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<C: Ciphersuite> {
|
||
|
|
/// The value committed to.
|
||
|
|
pub value: C::F,
|
||
|
|
/// The mask blinding the value committed to.
|
||
|
|
pub mask: C::F,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl<C: Ciphersuite> PedersenCommitment<C> {
|
||
|
|
/// 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<C: Ciphersuite> {
|
||
|
|
/// The values committed to across the `g` (bold) generators.
|
||
|
|
pub g_values: ScalarVector<C::F>,
|
||
|
|
/// The values committed to across the `h` (bold) generators.
|
||
|
|
pub h_values: ScalarVector<C::F>,
|
||
|
|
/// The mask blinding the values committed to.
|
||
|
|
pub mask: C::F,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl<C: Ciphersuite> PedersenVectorCommitment<C> {
|
||
|
|
/// 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<C::G> {
|
||
|
|
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)
|
||
|
|
}
|
||
|
|
}
|