mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-11 13:39:25 +00:00
Downstream the eVRF libraries from FCMP++
Also adds no-std support to secq256k1 and embedwards25519.
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
use std_shims::{vec, vec::Vec};
|
||||
|
||||
use rand_core::{RngCore, CryptoRng};
|
||||
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
@@ -20,10 +22,10 @@ pub use crate::lincomb::{Variable, LinComb};
|
||||
/// `aL * aR = aO, WL * aL + WR * aR + WO * aO = WV * V + c`.
|
||||
///
|
||||
/// Generalized Bulletproofs modifies this to
|
||||
/// `aL * aR = aO, WL * aL + WR * aR + WO * aO + WCG * C_G + WCH * C_H = WV * V + c`.
|
||||
/// `aL * aR = aO, WL * aL + WR * aR + WO * aO + WCG * C_G = WV * V + c`.
|
||||
///
|
||||
/// We implement the latter, yet represented (for simplicity) as
|
||||
/// `aL * aR = aO, WL * aL + WR * aR + WO * aO + WCG * C_G + WCH * C_H + WV * V + c = 0`.
|
||||
/// `aL * aR = aO, WL * aL + WR * aR + WO * aO + WCG * C_G + WV * V + c = 0`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArithmeticCircuitStatement<'a, C: Ciphersuite> {
|
||||
generators: ProofGenerators<'a, C>,
|
||||
@@ -202,16 +204,10 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
|
||||
if c.g_values.len() > n {
|
||||
Err(AcError::NotEnoughGenerators)?;
|
||||
}
|
||||
if c.h_values.len() > n {
|
||||
Err(AcError::NotEnoughGenerators)?;
|
||||
}
|
||||
// The Pedersen vector commitments internally have n terms
|
||||
while c.g_values.len() < n {
|
||||
c.g_values.0.push(C::F::ZERO);
|
||||
}
|
||||
while c.h_values.len() < n {
|
||||
c.h_values.0.push(C::F::ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
// Check the witness's consistency with the statement
|
||||
@@ -227,12 +223,7 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
|
||||
}
|
||||
}
|
||||
for (commitment, opening) in self.C.0.iter().zip(witness.c.iter()) {
|
||||
if Some(*commitment) !=
|
||||
opening.commit(
|
||||
self.generators.g_bold_slice(),
|
||||
self.generators.h_bold_slice(),
|
||||
self.generators.h(),
|
||||
)
|
||||
if Some(*commitment) != opening.commit(self.generators.g_bold_slice(), self.generators.h())
|
||||
{
|
||||
Err(AcError::InconsistentWitness)?;
|
||||
}
|
||||
@@ -250,11 +241,6 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
|
||||
weights.iter().map(|(j, weight)| *weight * c.g_values[*j])
|
||||
}),
|
||||
)
|
||||
.chain(
|
||||
constraint.WCH.iter().zip(&witness.c).flat_map(|(weights, c)| {
|
||||
weights.iter().map(|(j, weight)| *weight * c.h_values[*j])
|
||||
}),
|
||||
)
|
||||
.chain(constraint.WV.iter().map(|(i, weight)| *weight * witness.v[*i].value))
|
||||
.chain(core::iter::once(constraint.c))
|
||||
.sum::<C::F>();
|
||||
@@ -306,8 +292,8 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
|
||||
transcript.push_point(AI);
|
||||
transcript.push_point(AO);
|
||||
transcript.push_point(S);
|
||||
let y = transcript.challenge();
|
||||
let z = transcript.challenge();
|
||||
let y = transcript.challenge::<C>();
|
||||
let z = transcript.challenge::<C>();
|
||||
let YzChallenges { y_inv, z } = self.yz_challenges(y, z);
|
||||
let y = ScalarVector::powers(y, n);
|
||||
|
||||
@@ -318,7 +304,7 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
|
||||
// polynomial).
|
||||
|
||||
// ni = n'
|
||||
let ni = 2 * (c + 1);
|
||||
let ni = 2 + (2 * (c / 2));
|
||||
// These indexes are from the Generalized Bulletproofs paper
|
||||
#[rustfmt::skip]
|
||||
let ilr = ni / 2; // 1 if c = 0
|
||||
@@ -379,32 +365,25 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
|
||||
// r decreasing from n' (skipping jlr)
|
||||
|
||||
let mut cg_weights = Vec::with_capacity(witness.c.len());
|
||||
let mut ch_weights = Vec::with_capacity(witness.c.len());
|
||||
for i in 0 .. witness.c.len() {
|
||||
let mut cg = ScalarVector::new(n);
|
||||
let mut ch = ScalarVector::new(n);
|
||||
for (constraint, z) in self.constraints.iter().zip(&z.0) {
|
||||
if let Some(WCG) = constraint.WCG.get(i) {
|
||||
accumulate_vector(&mut cg, WCG, *z);
|
||||
}
|
||||
if let Some(WCH) = constraint.WCH.get(i) {
|
||||
accumulate_vector(&mut ch, WCH, *z);
|
||||
}
|
||||
}
|
||||
cg_weights.push(cg);
|
||||
ch_weights.push(ch);
|
||||
}
|
||||
|
||||
for (i, (c, (cg_weights, ch_weights))) in
|
||||
witness.c.iter().zip(cg_weights.into_iter().zip(ch_weights)).enumerate()
|
||||
{
|
||||
let i = i + 1;
|
||||
for (mut i, (c, cg_weights)) in witness.c.iter().zip(cg_weights).enumerate() {
|
||||
if i >= ilr {
|
||||
i += 1;
|
||||
}
|
||||
// Because i has skipped ilr, j will skip jlr
|
||||
let j = ni - i;
|
||||
|
||||
l[i] = c.g_values.clone();
|
||||
l[j] = ch_weights * &y_inv;
|
||||
r[j] = cg_weights;
|
||||
r[i] = (c.h_values.clone() * &y) + &r[i];
|
||||
}
|
||||
|
||||
// Multiply them to obtain t
|
||||
@@ -437,7 +416,7 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
|
||||
transcript.push_point(multiexp(&[(*t, self.generators.g()), (*tau, self.generators.h())]));
|
||||
}
|
||||
|
||||
let x: ScalarVector<C::F> = ScalarVector::powers(transcript.challenge(), t.len());
|
||||
let x: ScalarVector<C::F> = ScalarVector::powers(transcript.challenge::<C>(), t.len());
|
||||
|
||||
let poly_eval = |poly: &[ScalarVector<C::F>], x: &ScalarVector<_>| -> ScalarVector<_> {
|
||||
let mut res = ScalarVector::<C::F>::new(poly[0].0.len());
|
||||
@@ -477,8 +456,11 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
|
||||
let mut u = (alpha * x[ilr]) + (beta * x[io]) + (rho * x[is]);
|
||||
|
||||
// Incorporate the commitment masks multiplied by the associated power of x
|
||||
for (i, commitment) in witness.c.iter().enumerate() {
|
||||
let i = i + 1;
|
||||
for (mut i, commitment) in witness.c.iter().enumerate() {
|
||||
// If this index is ni / 2, skip it
|
||||
if i >= (ni / 2) {
|
||||
i += 1;
|
||||
}
|
||||
u += x[i] * commitment.mask;
|
||||
}
|
||||
u
|
||||
@@ -498,7 +480,7 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
|
||||
transcript.push_scalar(tau_x);
|
||||
transcript.push_scalar(u);
|
||||
transcript.push_scalar(t_caret);
|
||||
let ip_x = transcript.challenge();
|
||||
let ip_x = transcript.challenge::<C>();
|
||||
P_terms.push((ip_x * t_caret, self.generators.g()));
|
||||
IpStatement::new(
|
||||
self.generators,
|
||||
@@ -513,16 +495,27 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
|
||||
}
|
||||
|
||||
/// Verify a proof for this statement.
|
||||
///
|
||||
/// This solely queues the statement for batch verification. The resulting BatchVerifier MUST
|
||||
/// still be verified.
|
||||
///
|
||||
/// If this proof returns an error, the BatchVerifier MUST be assumed corrupted and discarded.
|
||||
pub fn verify<R: RngCore + CryptoRng>(
|
||||
self,
|
||||
rng: &mut R,
|
||||
verifier: &mut BatchVerifier<C>,
|
||||
transcript: &mut VerifierTranscript,
|
||||
) -> Result<(), AcError> {
|
||||
if verifier.g_bold.len() < self.generators.len() {
|
||||
verifier.g_bold.resize(self.generators.len(), C::F::ZERO);
|
||||
verifier.h_bold.resize(self.generators.len(), C::F::ZERO);
|
||||
verifier.h_sum.resize(self.generators.len(), C::F::ZERO);
|
||||
}
|
||||
|
||||
let n = self.n();
|
||||
let c = self.c();
|
||||
|
||||
let ni = 2 * (c + 1);
|
||||
let ni = 2 + (2 * (c / 2));
|
||||
|
||||
let ilr = ni / 2;
|
||||
let io = ni;
|
||||
@@ -535,8 +528,8 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
|
||||
let AI = transcript.read_point::<C>().map_err(|_| AcError::IncompleteProof)?;
|
||||
let AO = transcript.read_point::<C>().map_err(|_| AcError::IncompleteProof)?;
|
||||
let S = transcript.read_point::<C>().map_err(|_| AcError::IncompleteProof)?;
|
||||
let y = transcript.challenge();
|
||||
let z = transcript.challenge();
|
||||
let y = transcript.challenge::<C>();
|
||||
let z = transcript.challenge::<C>();
|
||||
let YzChallenges { y_inv, z } = self.yz_challenges(y, z);
|
||||
|
||||
let mut l_weights = ScalarVector::new(n);
|
||||
@@ -559,7 +552,7 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
|
||||
for _ in 0 .. (t_poly_len - ni - 1) {
|
||||
T_after_ni.push(transcript.read_point::<C>().map_err(|_| AcError::IncompleteProof)?);
|
||||
}
|
||||
let x: ScalarVector<C::F> = ScalarVector::powers(transcript.challenge(), t_poly_len);
|
||||
let x: ScalarVector<C::F> = ScalarVector::powers(transcript.challenge::<C>(), t_poly_len);
|
||||
|
||||
let tau_x = transcript.read_scalar::<C>().map_err(|_| AcError::IncompleteProof)?;
|
||||
let u = transcript.read_scalar::<C>().map_err(|_| AcError::IncompleteProof)?;
|
||||
@@ -624,34 +617,25 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
|
||||
h_bold_scalars = h_bold_scalars + &(o_weights * verifier_weight);
|
||||
|
||||
let mut cg_weights = Vec::with_capacity(self.C.len());
|
||||
let mut ch_weights = Vec::with_capacity(self.C.len());
|
||||
for i in 0 .. self.C.len() {
|
||||
let mut cg = ScalarVector::new(n);
|
||||
let mut ch = ScalarVector::new(n);
|
||||
for (constraint, z) in self.constraints.iter().zip(&z.0) {
|
||||
if let Some(WCG) = constraint.WCG.get(i) {
|
||||
accumulate_vector(&mut cg, WCG, *z);
|
||||
}
|
||||
if let Some(WCH) = constraint.WCH.get(i) {
|
||||
accumulate_vector(&mut ch, WCH, *z);
|
||||
}
|
||||
}
|
||||
cg_weights.push(cg);
|
||||
ch_weights.push(ch);
|
||||
}
|
||||
|
||||
// Push the terms for C, which increment from 0, and the terms for WC, which decrement from
|
||||
// n'
|
||||
for (i, (C, (WCG, WCH))) in
|
||||
self.C.0.into_iter().zip(cg_weights.into_iter().zip(ch_weights)).enumerate()
|
||||
{
|
||||
let i = i + 1;
|
||||
for (mut i, (C, WCG)) in self.C.0.into_iter().zip(cg_weights).enumerate() {
|
||||
if i >= (ni / 2) {
|
||||
i += 1;
|
||||
}
|
||||
let j = ni - i;
|
||||
verifier.additional.push((x[i], C));
|
||||
h_bold_scalars = h_bold_scalars + &(WCG * x[j]);
|
||||
for (i, scalar) in (WCH * &y_inv * x[j]).0.into_iter().enumerate() {
|
||||
verifier.g_bold[i] += scalar;
|
||||
}
|
||||
}
|
||||
|
||||
// All terms for h_bold here have actually been for h_bold', h_bold * y_inv
|
||||
@@ -666,7 +650,7 @@ impl<'a, C: Ciphersuite> ArithmeticCircuitStatement<'a, C> {
|
||||
|
||||
// Prove for lines 88, 92 with an Inner-Product statement
|
||||
// This inlines Protocol 1, as our IpStatement implements Protocol 2
|
||||
let ip_x = transcript.challenge();
|
||||
let ip_x = transcript.challenge::<C>();
|
||||
// P is amended with this additional term
|
||||
verifier.g += verifier_weight * ip_x * t_caret;
|
||||
IpStatement::new(self.generators, y_inv, ip_x, P::Verifier { verifier_weight })
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std_shims::{vec, vec::Vec};
|
||||
|
||||
use multiexp::multiexp_vartime;
|
||||
use ciphersuite::{group::ff::Field, Ciphersuite};
|
||||
|
||||
@@ -186,7 +188,7 @@ impl<'a, C: Ciphersuite> IpStatement<'a, C> {
|
||||
// Now that we've calculate L, R, transcript them to receive x (26-27)
|
||||
transcript.push_point(L);
|
||||
transcript.push_point(R);
|
||||
let x: C::F = transcript.challenge();
|
||||
let x: C::F = transcript.challenge::<C>();
|
||||
let x_inv = x.invert().unwrap();
|
||||
|
||||
// The prover and verifier now calculate the following (28-31)
|
||||
@@ -269,11 +271,19 @@ impl<'a, C: Ciphersuite> IpStatement<'a, C> {
|
||||
/// This will return Err if there is an error. This will return Ok if the proof was successfully
|
||||
/// queued for batch verification. The caller is required to verify the batch in order to ensure
|
||||
/// the proof is actually correct.
|
||||
///
|
||||
/// If this proof returns an error, the BatchVerifier MUST be assumed corrupted and discarded.
|
||||
pub(crate) fn verify(
|
||||
self,
|
||||
verifier: &mut BatchVerifier<C>,
|
||||
transcript: &mut VerifierTranscript,
|
||||
) -> Result<(), IpError> {
|
||||
if verifier.g_bold.len() < self.generators.len() {
|
||||
verifier.g_bold.resize(self.generators.len(), C::F::ZERO);
|
||||
verifier.h_bold.resize(self.generators.len(), C::F::ZERO);
|
||||
verifier.h_sum.resize(self.generators.len(), C::F::ZERO);
|
||||
}
|
||||
|
||||
let IpStatement { generators, h_bold_weights, u, P } = self;
|
||||
|
||||
// Calculate the discrete log w.r.t. 2 for the amount of generators present
|
||||
@@ -296,7 +306,7 @@ impl<'a, C: Ciphersuite> IpStatement<'a, C> {
|
||||
for _ in 0 .. lr_len {
|
||||
L.push(transcript.read_point::<C>().map_err(|_| IpError::IncompleteProof)?);
|
||||
R.push(transcript.read_point::<C>().map_err(|_| IpError::IncompleteProof)?);
|
||||
xs.push(transcript.challenge());
|
||||
xs.push(transcript.challenge::<C>());
|
||||
}
|
||||
|
||||
// We calculate their inverse in batch
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![deny(missing_docs)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::fmt;
|
||||
use std::collections::HashSet;
|
||||
use std_shims::{vec, vec::Vec, collections::HashSet};
|
||||
|
||||
use zeroize::Zeroize;
|
||||
|
||||
@@ -70,14 +71,26 @@ pub struct Generators<C: Ciphersuite> {
|
||||
#[must_use]
|
||||
#[derive(Clone)]
|
||||
pub struct BatchVerifier<C: Ciphersuite> {
|
||||
g: C::F,
|
||||
h: C::F,
|
||||
/// The summed scalar for the G generator.
|
||||
pub g: C::F,
|
||||
/// The summed scalar for the G generator.
|
||||
pub h: C::F,
|
||||
|
||||
g_bold: Vec<C::F>,
|
||||
h_bold: Vec<C::F>,
|
||||
h_sum: Vec<C::F>,
|
||||
/// The summed scalars for the G_bold generators.
|
||||
pub g_bold: Vec<C::F>,
|
||||
/// The summed scalars for the H_bold generators.
|
||||
pub h_bold: Vec<C::F>,
|
||||
/// The summed scalars for the sums of all H generators prior to the index.
|
||||
///
|
||||
/// This is not populated with the full set of summed H generators. This is only populated with
|
||||
/// the powers of 2. Accordingly, an index i specifies a scalar for the sum of all H generators
|
||||
/// from H**2**0 ..= H**2**i.
|
||||
pub h_sum: Vec<C::F>,
|
||||
|
||||
additional: Vec<(C::F, C::G)>,
|
||||
/// Additional (non-fixed) points to include in the multiexp.
|
||||
///
|
||||
/// This is used for proof-specific elements.
|
||||
pub additional: Vec<(C::F, C::G)>,
|
||||
}
|
||||
|
||||
impl<C: Ciphersuite> fmt::Debug for Generators<C> {
|
||||
@@ -171,15 +184,15 @@ impl<C: Ciphersuite> Generators<C> {
|
||||
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> {
|
||||
/// Create a BatchVerifier for proofs which use a consistent set of generators.
|
||||
pub fn batch_verifier() -> 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()],
|
||||
g_bold: vec![],
|
||||
h_bold: vec![],
|
||||
h_sum: vec![],
|
||||
|
||||
additional: Vec::with_capacity(128),
|
||||
}
|
||||
@@ -298,8 +311,6 @@ impl<C: Ciphersuite> PedersenCommitment<C> {
|
||||
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,
|
||||
}
|
||||
@@ -309,8 +320,8 @@ impl<C: Ciphersuite> PedersenVectorCommitment<C> {
|
||||
///
|
||||
/// 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()) {
|
||||
pub fn commit(&self, g_bold: &[C::G], h: C::G) -> Option<C::G> {
|
||||
if g_bold.len() < self.g_values.len() {
|
||||
None?;
|
||||
};
|
||||
|
||||
@@ -318,9 +329,6 @@ impl<C: Ciphersuite> PedersenVectorCommitment<C> {
|
||||
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)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use core::ops::{Add, Sub, Mul};
|
||||
use std_shims::{vec, vec::Vec};
|
||||
|
||||
use zeroize::Zeroize;
|
||||
|
||||
@@ -23,13 +24,6 @@ pub enum Variable {
|
||||
/// The index of the variable.
|
||||
index: usize,
|
||||
},
|
||||
/// A variable within a Pedersen vector commitment, committed to with a generator from `h` (bold).
|
||||
CH {
|
||||
/// The commitment being indexed.
|
||||
commitment: usize,
|
||||
/// The index of the variable.
|
||||
index: usize,
|
||||
},
|
||||
/// A variable within a Pedersen commitment.
|
||||
V(usize),
|
||||
}
|
||||
@@ -41,7 +35,7 @@ impl Zeroize for Variable {
|
||||
|
||||
/// A linear combination.
|
||||
///
|
||||
/// Specifically, `WL aL + WR aR + WO aO + WCG C_G + WCH C_H + WV V + c`.
|
||||
/// Specifically, `WL aL + WR aR + WO aO + WCG C_G + WV V + c`.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
|
||||
#[must_use]
|
||||
pub struct LinComb<F: PrimeField> {
|
||||
@@ -55,7 +49,6 @@ pub struct LinComb<F: PrimeField> {
|
||||
pub(crate) WO: Vec<(usize, F)>,
|
||||
// Sparse representation once within a commitment
|
||||
pub(crate) WCG: Vec<Vec<(usize, F)>>,
|
||||
pub(crate) WCH: Vec<Vec<(usize, F)>>,
|
||||
// Sparse representation of WV
|
||||
pub(crate) WV: Vec<(usize, F)>,
|
||||
pub(crate) c: F,
|
||||
@@ -81,15 +74,9 @@ impl<F: PrimeField> Add<&LinComb<F>> for LinComb<F> {
|
||||
while self.WCG.len() < constraint.WCG.len() {
|
||||
self.WCG.push(vec![]);
|
||||
}
|
||||
while self.WCH.len() < constraint.WCH.len() {
|
||||
self.WCH.push(vec![]);
|
||||
}
|
||||
for (sWC, cWC) in self.WCG.iter_mut().zip(&constraint.WCG) {
|
||||
sWC.extend(cWC);
|
||||
}
|
||||
for (sWC, cWC) in self.WCH.iter_mut().zip(&constraint.WCH) {
|
||||
sWC.extend(cWC);
|
||||
}
|
||||
self.WV.extend(&constraint.WV);
|
||||
self.c += constraint.c;
|
||||
self
|
||||
@@ -110,15 +97,9 @@ impl<F: PrimeField> Sub<&LinComb<F>> for LinComb<F> {
|
||||
while self.WCG.len() < constraint.WCG.len() {
|
||||
self.WCG.push(vec![]);
|
||||
}
|
||||
while self.WCH.len() < constraint.WCH.len() {
|
||||
self.WCH.push(vec![]);
|
||||
}
|
||||
for (sWC, cWC) in self.WCG.iter_mut().zip(&constraint.WCG) {
|
||||
sWC.extend(cWC.iter().map(|(i, weight)| (*i, -*weight)));
|
||||
}
|
||||
for (sWC, cWC) in self.WCH.iter_mut().zip(&constraint.WCH) {
|
||||
sWC.extend(cWC.iter().map(|(i, weight)| (*i, -*weight)));
|
||||
}
|
||||
self.WV.extend(constraint.WV.iter().map(|(i, weight)| (*i, -*weight)));
|
||||
self.c -= constraint.c;
|
||||
self
|
||||
@@ -143,11 +124,6 @@ impl<F: PrimeField> Mul<F> for LinComb<F> {
|
||||
*weight *= scalar;
|
||||
}
|
||||
}
|
||||
for WC in self.WCH.iter_mut() {
|
||||
for (_, weight) in WC {
|
||||
*weight *= scalar;
|
||||
}
|
||||
}
|
||||
for (_, weight) in self.WV.iter_mut() {
|
||||
*weight *= scalar;
|
||||
}
|
||||
@@ -167,7 +143,6 @@ impl<F: PrimeField> LinComb<F> {
|
||||
WR: vec![],
|
||||
WO: vec![],
|
||||
WCG: vec![],
|
||||
WCH: vec![],
|
||||
WV: vec![],
|
||||
c: F::ZERO,
|
||||
}
|
||||
@@ -196,14 +171,6 @@ impl<F: PrimeField> LinComb<F> {
|
||||
}
|
||||
self.WCG[i].push((j, scalar))
|
||||
}
|
||||
Variable::CH { commitment: i, index: j } => {
|
||||
self.highest_c_index = self.highest_c_index.max(Some(i));
|
||||
self.highest_a_index = self.highest_a_index.max(Some(j));
|
||||
while self.WCH.len() <= i {
|
||||
self.WCH.push(vec![]);
|
||||
}
|
||||
self.WCH[i].push((j, scalar))
|
||||
}
|
||||
Variable::V(i) => {
|
||||
self.highest_v_index = self.highest_v_index.max(Some(i));
|
||||
self.WV.push((i, scalar));
|
||||
@@ -238,11 +205,6 @@ impl<F: PrimeField> LinComb<F> {
|
||||
&self.WCG
|
||||
}
|
||||
|
||||
/// View the current weights for CH.
|
||||
pub fn WCH(&self) -> &[Vec<(usize, F)>] {
|
||||
&self.WCH
|
||||
}
|
||||
|
||||
/// View the current weights for V.
|
||||
pub fn WV(&self) -> &[(usize, F)] {
|
||||
&self.WV
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use core::ops::{Index, IndexMut};
|
||||
use std_shims::vec::Vec;
|
||||
|
||||
use zeroize::Zeroize;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use core::ops::{Index, IndexMut, Add, Sub, Mul};
|
||||
use std_shims::{vec, vec::Vec};
|
||||
|
||||
use zeroize::Zeroize;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use rand_core::{RngCore, OsRng};
|
||||
use ciphersuite::{group::ff::Field, Ciphersuite, Ristretto};
|
||||
|
||||
use crate::{
|
||||
ScalarVector, PedersenCommitment, PedersenVectorCommitment,
|
||||
ScalarVector, PedersenCommitment, PedersenVectorCommitment, Generators,
|
||||
transcript::*,
|
||||
arithmetic_circuit_proof::{
|
||||
Variable, LinComb, ArithmeticCircuitStatement, ArithmeticCircuitWitness,
|
||||
@@ -43,7 +43,7 @@ fn test_zero_arithmetic_circuit() {
|
||||
statement.clone().prove(&mut OsRng, &mut transcript, witness).unwrap();
|
||||
transcript.complete()
|
||||
};
|
||||
let mut verifier = generators.batch_verifier();
|
||||
let mut verifier = Generators::batch_verifier();
|
||||
|
||||
let mut transcript = VerifierTranscript::new([0; 32], &proof);
|
||||
let verifier_commmitments = transcript.read_commitments(0, 1);
|
||||
@@ -59,14 +59,8 @@ fn test_vector_commitment_arithmetic_circuit() {
|
||||
|
||||
let v1 = <Ristretto as Ciphersuite>::F::random(&mut OsRng);
|
||||
let v2 = <Ristretto as Ciphersuite>::F::random(&mut OsRng);
|
||||
let v3 = <Ristretto as Ciphersuite>::F::random(&mut OsRng);
|
||||
let v4 = <Ristretto as Ciphersuite>::F::random(&mut OsRng);
|
||||
let gamma = <Ristretto as Ciphersuite>::F::random(&mut OsRng);
|
||||
let commitment = (reduced.g_bold(0) * v1) +
|
||||
(reduced.g_bold(1) * v2) +
|
||||
(reduced.h_bold(0) * v3) +
|
||||
(reduced.h_bold(1) * v4) +
|
||||
(generators.h() * gamma);
|
||||
let commitment = (reduced.g_bold(0) * v1) + (reduced.g_bold(1) * v2) + (generators.h() * gamma);
|
||||
let V = vec![];
|
||||
let C = vec![commitment];
|
||||
|
||||
@@ -83,20 +77,14 @@ fn test_vector_commitment_arithmetic_circuit() {
|
||||
vec![LinComb::empty()
|
||||
.term(<Ristretto as Ciphersuite>::F::ONE, Variable::CG { commitment: 0, index: 0 })
|
||||
.term(<Ristretto as Ciphersuite>::F::from(2u64), Variable::CG { commitment: 0, index: 1 })
|
||||
.term(<Ristretto as Ciphersuite>::F::from(3u64), Variable::CH { commitment: 0, index: 0 })
|
||||
.term(<Ristretto as Ciphersuite>::F::from(4u64), Variable::CH { commitment: 0, index: 1 })
|
||||
.constant(-(v1 + (v2 + v2) + (v3 + v3 + v3) + (v4 + v4 + v4 + v4)))],
|
||||
.constant(-(v1 + (v2 + v2)))],
|
||||
commitments.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
let witness = ArithmeticCircuitWitness::<Ristretto>::new(
|
||||
aL,
|
||||
aR,
|
||||
vec![PedersenVectorCommitment {
|
||||
g_values: ScalarVector(vec![v1, v2]),
|
||||
h_values: ScalarVector(vec![v3, v4]),
|
||||
mask: gamma,
|
||||
}],
|
||||
vec![PedersenVectorCommitment { g_values: ScalarVector(vec![v1, v2]), mask: gamma }],
|
||||
vec![],
|
||||
)
|
||||
.unwrap();
|
||||
@@ -105,7 +93,7 @@ fn test_vector_commitment_arithmetic_circuit() {
|
||||
statement.clone().prove(&mut OsRng, &mut transcript, witness).unwrap();
|
||||
transcript.complete()
|
||||
};
|
||||
let mut verifier = generators.batch_verifier();
|
||||
let mut verifier = Generators::batch_verifier();
|
||||
|
||||
let mut transcript = VerifierTranscript::new([0; 32], &proof);
|
||||
let verifier_commmitments = transcript.read_commitments(1, 0);
|
||||
@@ -139,13 +127,8 @@ fn fuzz_test_arithmetic_circuit() {
|
||||
while g_values.0.len() < ((OsRng.next_u64() % 8) + 1).try_into().unwrap() {
|
||||
g_values.0.push(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
||||
}
|
||||
let mut h_values = ScalarVector(vec![]);
|
||||
while h_values.0.len() < ((OsRng.next_u64() % 8) + 1).try_into().unwrap() {
|
||||
h_values.0.push(<Ristretto as Ciphersuite>::F::random(&mut OsRng));
|
||||
}
|
||||
C.push(PedersenVectorCommitment {
|
||||
g_values,
|
||||
h_values,
|
||||
mask: <Ristretto as Ciphersuite>::F::random(&mut OsRng),
|
||||
});
|
||||
}
|
||||
@@ -193,13 +176,6 @@ fn fuzz_test_arithmetic_circuit() {
|
||||
constraint = constraint.term(weight, Variable::CG { commitment, index });
|
||||
eval += weight * C.g_values[index];
|
||||
}
|
||||
|
||||
for _ in 0 .. (OsRng.next_u64() % 4) {
|
||||
let index = usize::try_from(OsRng.next_u64()).unwrap() % C.h_values.len();
|
||||
let weight = <Ristretto as Ciphersuite>::F::random(&mut OsRng);
|
||||
constraint = constraint.term(weight, Variable::CH { commitment, index });
|
||||
eval += weight * C.h_values[index];
|
||||
}
|
||||
}
|
||||
|
||||
if !V.is_empty() {
|
||||
@@ -218,11 +194,7 @@ fn fuzz_test_arithmetic_circuit() {
|
||||
|
||||
let mut transcript = Transcript::new([0; 32]);
|
||||
let commitments = transcript.write_commitments(
|
||||
C.iter()
|
||||
.map(|C| {
|
||||
C.commit(generators.g_bold_slice(), generators.h_bold_slice(), generators.h()).unwrap()
|
||||
})
|
||||
.collect(),
|
||||
C.iter().map(|C| C.commit(generators.g_bold_slice(), generators.h()).unwrap()).collect(),
|
||||
V.iter().map(|V| V.commit(generators.g(), generators.h())).collect(),
|
||||
);
|
||||
|
||||
@@ -239,7 +211,7 @@ fn fuzz_test_arithmetic_circuit() {
|
||||
statement.clone().prove(&mut OsRng, &mut transcript, witness).unwrap();
|
||||
transcript.complete()
|
||||
};
|
||||
let mut verifier = generators.batch_verifier();
|
||||
let mut verifier = Generators::batch_verifier();
|
||||
|
||||
let mut transcript = VerifierTranscript::new([0; 32], &proof);
|
||||
let verifier_commmitments = transcript.read_commitments(C.len(), V.len());
|
||||
|
||||
@@ -8,7 +8,7 @@ use ciphersuite::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ScalarVector, PointVector,
|
||||
ScalarVector, PointVector, Generators,
|
||||
transcript::*,
|
||||
inner_product::{P, IpStatement, IpWitness},
|
||||
tests::generators,
|
||||
@@ -41,7 +41,7 @@ fn test_zero_inner_product() {
|
||||
transcript.complete()
|
||||
};
|
||||
|
||||
let mut verifier = generators.batch_verifier();
|
||||
let mut verifier = Generators::batch_verifier();
|
||||
IpStatement::<Ristretto>::new(
|
||||
reduced,
|
||||
ScalarVector(vec![<Ristretto as Ciphersuite>::F::ONE; 1]),
|
||||
@@ -58,7 +58,7 @@ fn test_zero_inner_product() {
|
||||
fn test_inner_product() {
|
||||
// P = sum(g_bold * a, h_bold * b)
|
||||
let generators = generators::<Ristretto>(32);
|
||||
let mut verifier = generators.batch_verifier();
|
||||
let mut verifier = Generators::batch_verifier();
|
||||
for i in [1, 2, 4, 8, 16, 32] {
|
||||
let generators = generators.reduce(i).unwrap();
|
||||
let g = generators.g();
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use std::io;
|
||||
use std_shims::{vec::Vec, io};
|
||||
|
||||
use blake2::{Digest, Blake2b512};
|
||||
|
||||
use ciphersuite::{
|
||||
group::{ff::PrimeField, GroupEncoding},
|
||||
group::{
|
||||
ff::{Field, PrimeField},
|
||||
GroupEncoding,
|
||||
},
|
||||
Ciphersuite,
|
||||
};
|
||||
|
||||
@@ -13,27 +16,11 @@ const SCALAR: u8 = 0;
|
||||
const POINT: u8 = 1;
|
||||
const CHALLENGE: u8 = 2;
|
||||
|
||||
fn challenge<F: PrimeField>(digest: &mut Blake2b512) -> F {
|
||||
// Panic if this is such a wide field, we won't successfully perform a reduction into an unbiased
|
||||
// scalar
|
||||
debug_assert!((F::NUM_BITS + 128) < 512);
|
||||
|
||||
fn challenge<C: Ciphersuite>(digest: &mut Blake2b512) -> C::F {
|
||||
digest.update([CHALLENGE]);
|
||||
let chl = digest.clone().finalize();
|
||||
let chl = digest.clone().finalize().into();
|
||||
|
||||
let mut res = F::ZERO;
|
||||
for (i, mut byte) in chl.iter().cloned().enumerate() {
|
||||
for j in 0 .. 8 {
|
||||
let lsb = byte & 1;
|
||||
let mut bit = F::from(u64::from(lsb));
|
||||
for _ in 0 .. ((i * 8) + j) {
|
||||
bit = bit.double();
|
||||
}
|
||||
res += bit;
|
||||
|
||||
byte >>= 1;
|
||||
}
|
||||
}
|
||||
let res = C::reduce_512(chl);
|
||||
|
||||
// Negligible probability
|
||||
if bool::from(res.is_zero()) {
|
||||
@@ -83,6 +70,8 @@ impl Transcript {
|
||||
}
|
||||
|
||||
/// Push a scalar onto the transcript.
|
||||
///
|
||||
/// The order and layout of this must be constant to the context.
|
||||
pub fn push_scalar(&mut self, scalar: impl PrimeField) {
|
||||
self.digest.update([SCALAR]);
|
||||
let bytes = scalar.to_repr();
|
||||
@@ -91,6 +80,8 @@ impl Transcript {
|
||||
}
|
||||
|
||||
/// Push a point onto the transcript.
|
||||
///
|
||||
/// The order and layout of this must be constant to the context.
|
||||
pub fn push_point(&mut self, point: impl GroupEncoding) {
|
||||
self.digest.update([POINT]);
|
||||
let bytes = point.to_bytes();
|
||||
@@ -104,9 +95,11 @@ impl Transcript {
|
||||
C: Vec<C::G>,
|
||||
V: Vec<C::G>,
|
||||
) -> Commitments<C> {
|
||||
self.digest.update(u32::try_from(C.len()).unwrap().to_le_bytes());
|
||||
for C in &C {
|
||||
self.push_point(*C);
|
||||
}
|
||||
self.digest.update(u32::try_from(V.len()).unwrap().to_le_bytes());
|
||||
for V in &V {
|
||||
self.push_point(*V);
|
||||
}
|
||||
@@ -114,8 +107,14 @@ impl Transcript {
|
||||
}
|
||||
|
||||
/// Sample a challenge.
|
||||
pub fn challenge<F: PrimeField>(&mut self) -> F {
|
||||
challenge(&mut self.digest)
|
||||
pub fn challenge<C: Ciphersuite>(&mut self) -> C::F {
|
||||
challenge::<C>(&mut self.digest)
|
||||
}
|
||||
|
||||
/// Sample a challenge as a byte array.
|
||||
pub fn challenge_bytes(&mut self) -> [u8; 64] {
|
||||
self.digest.update([CHALLENGE]);
|
||||
self.digest.clone().finalize().into()
|
||||
}
|
||||
|
||||
/// Complete a transcript, yielding the fully serialized proof.
|
||||
@@ -139,20 +138,36 @@ impl<'a> VerifierTranscript<'a> {
|
||||
}
|
||||
|
||||
/// Read a scalar from the transcript.
|
||||
///
|
||||
/// The order and layout of this must be constant to the context.
|
||||
pub fn read_scalar<C: Ciphersuite>(&mut self) -> io::Result<C::F> {
|
||||
let scalar = C::read_F(&mut self.transcript)?;
|
||||
// Read the scalar onto the transcript using the serialization present in the transcript
|
||||
self.digest.update([SCALAR]);
|
||||
let bytes = scalar.to_repr();
|
||||
self.digest.update(bytes);
|
||||
let scalar_len = <C::F as PrimeField>::Repr::default().as_ref().len();
|
||||
if self.transcript.len() < scalar_len {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "not enough bytes to read_scalar"))?;
|
||||
}
|
||||
self.digest.update(&self.transcript[.. scalar_len]);
|
||||
|
||||
// Read the actual scalar, where `read_F` ensures its canonically serialized
|
||||
let scalar = C::read_F(&mut self.transcript)?;
|
||||
Ok(scalar)
|
||||
}
|
||||
|
||||
/// Read a point from the transcript.
|
||||
///
|
||||
/// The order and layout of this must be constant to the context.
|
||||
pub fn read_point<C: Ciphersuite>(&mut self) -> io::Result<C::G> {
|
||||
let point = C::read_G(&mut self.transcript)?;
|
||||
// Read the point onto the transcript using the serialization present in the transcript
|
||||
self.digest.update([POINT]);
|
||||
let bytes = point.to_bytes();
|
||||
self.digest.update(bytes);
|
||||
let point_len = <C::G as GroupEncoding>::Repr::default().as_ref().len();
|
||||
if self.transcript.len() < point_len {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "not enough bytes to read_point"))?;
|
||||
}
|
||||
self.digest.update(&self.transcript[.. point_len]);
|
||||
|
||||
// Read the actual point, where `read_G` ensures its canonically serialized
|
||||
let point = C::read_G(&mut self.transcript)?;
|
||||
Ok(point)
|
||||
}
|
||||
|
||||
@@ -165,10 +180,12 @@ impl<'a> VerifierTranscript<'a> {
|
||||
C: usize,
|
||||
V: usize,
|
||||
) -> io::Result<Commitments<C>> {
|
||||
self.digest.update(u32::try_from(C).unwrap().to_le_bytes());
|
||||
let mut C_vec = Vec::with_capacity(C);
|
||||
for _ in 0 .. C {
|
||||
C_vec.push(self.read_point::<C>()?);
|
||||
}
|
||||
self.digest.update(u32::try_from(V).unwrap().to_le_bytes());
|
||||
let mut V_vec = Vec::with_capacity(V);
|
||||
for _ in 0 .. V {
|
||||
V_vec.push(self.read_point::<C>()?);
|
||||
@@ -177,11 +194,17 @@ impl<'a> VerifierTranscript<'a> {
|
||||
}
|
||||
|
||||
/// Sample a challenge.
|
||||
pub fn challenge<F: PrimeField>(&mut self) -> F {
|
||||
challenge(&mut self.digest)
|
||||
pub fn challenge<C: Ciphersuite>(&mut self) -> C::F {
|
||||
challenge::<C>(&mut self.digest)
|
||||
}
|
||||
|
||||
/// Complete the transcript, returning the advanced slice.
|
||||
/// Sample a challenge as a byte array.
|
||||
pub fn challenge_bytes(&mut self) -> [u8; 64] {
|
||||
self.digest.update([CHALLENGE]);
|
||||
self.digest.clone().finalize().into()
|
||||
}
|
||||
|
||||
/// Complete the transcript transcript, yielding what remains.
|
||||
pub fn complete(self) -> &'a [u8] {
|
||||
self.transcript
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user