Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[zk-sdk] Add range_proof module #1091

Merged
merged 5 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions zk-sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#[cfg(not(target_os = "solana"))]
pub mod encryption;
pub mod errors;
mod range_proof;
#[cfg(not(target_os = "solana"))]
mod sigma_proofs;
#[cfg(not(target_os = "solana"))]
Expand Down
46 changes: 46 additions & 0 deletions zk-sdk/src/range_proof/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! Errors related to proving and verifying range proofs.
use {crate::errors::TranscriptError, thiserror::Error};

#[cfg(not(target_os = "solana"))]
#[derive(Error, Clone, Debug, Eq, PartialEq)]
pub enum RangeProofGenerationError {
#[error("maximum generator length exceeded")]
MaximumGeneratorLengthExceeded,
#[error("amounts, commitments, openings, or bit lengths vectors have different lengths")]
VectorLengthMismatch,
#[error("invalid bit size")]
InvalidBitSize,
#[error("insufficient generators for the proof")]
GeneratorLengthMismatch,
#[error("inner product length mismatch")]
InnerProductLengthMismatch,
}

#[derive(Error, Clone, Debug, Eq, PartialEq)]
pub enum RangeProofVerificationError {
#[error("required algebraic relation does not hold")]
AlgebraicRelation,
#[error("malformed proof")]
Deserialization,
#[error("multiscalar multiplication failed")]
MultiscalarMul,
#[error("transcript failed to produce a challenge")]
Transcript(#[from] TranscriptError),
#[error(
"attempted to verify range proof with a non-power-of-two bit size or bit size is too big"
)]
InvalidBitSize,
#[error("insufficient generators for the proof")]
InvalidGeneratorsLength,
#[error("maximum generator length exceeded")]
MaximumGeneratorLengthExceeded,
#[error("commitments and bit lengths vectors have different lengths")]
VectorLengthMismatch,
}

#[cfg(not(target_os = "solana"))]
#[derive(Error, Clone, Debug, Eq, PartialEq)]
pub enum RangeProofGeneratorError {
#[error("maximum generator length exceeded")]
MaximumGeneratorLengthExceeded,
}
155 changes: 155 additions & 0 deletions zk-sdk/src/range_proof/generators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
use {
crate::range_proof::errors::RangeProofGeneratorError,
curve25519_dalek::{
digest::{ExtendableOutput, Update, XofReader},
ristretto::RistrettoPoint,
},
sha3::{Sha3XofReader, Shake256},
};

const MAX_GENERATOR_LENGTH: usize = u32::MAX as usize;

/// Generators for Pedersen vector commitments that are used for inner-product proofs.
struct GeneratorsChain {
reader: Sha3XofReader,
}

impl GeneratorsChain {
/// Creates a chain of generators, determined by the hash of `label`.
fn new(label: &[u8]) -> Self {
let mut shake = Shake256::default();
shake.update(b"GeneratorsChain");
shake.update(label);

GeneratorsChain {
reader: shake.finalize_xof(),
}
}

/// Advances the reader n times, squeezing and discarding
/// the result.
fn fast_forward(mut self, n: usize) -> Self {
for _ in 0..n {
let mut buf = [0u8; 64];
self.reader.read(&mut buf);
}
self
}
}

impl Default for GeneratorsChain {
fn default() -> Self {
Self::new(&[])
}
}

impl Iterator for GeneratorsChain {
type Item = RistrettoPoint;

fn next(&mut self) -> Option<Self::Item> {
let mut uniform_bytes = [0u8; 64];
self.reader.read(&mut uniform_bytes);

Some(RistrettoPoint::from_uniform_bytes(&uniform_bytes))
}

fn size_hint(&self) -> (usize, Option<usize>) {
(usize::max_value(), None)
}
}

#[allow(non_snake_case)]
#[derive(Clone)]
pub struct RangeProofGens {
/// The maximum number of usable generators.
pub gens_capacity: usize,
/// Precomputed \\(\mathbf G\\) generators.
G_vec: Vec<RistrettoPoint>,
/// Precomputed \\(\mathbf H\\) generators.
H_vec: Vec<RistrettoPoint>,
}

impl RangeProofGens {
pub fn new(gens_capacity: usize) -> Result<Self, RangeProofGeneratorError> {
let mut gens = RangeProofGens {
gens_capacity: 0,
G_vec: Vec::new(),
H_vec: Vec::new(),
};
gens.increase_capacity(gens_capacity)?;
Ok(gens)
}

/// Increases the generators' capacity to the amount specified.
/// If less than or equal to the current capacity, does nothing.
pub fn increase_capacity(
&mut self,
new_capacity: usize,
) -> Result<(), RangeProofGeneratorError> {
if self.gens_capacity >= new_capacity {
return Ok(());
}

if new_capacity > MAX_GENERATOR_LENGTH {
return Err(RangeProofGeneratorError::MaximumGeneratorLengthExceeded);
}

self.G_vec.extend(
&mut GeneratorsChain::new(&[b'G'])
.fast_forward(self.gens_capacity)
.take(new_capacity - self.gens_capacity),
);

self.H_vec.extend(
&mut GeneratorsChain::new(&[b'H'])
.fast_forward(self.gens_capacity)
.take(new_capacity - self.gens_capacity),
);

self.gens_capacity = new_capacity;
Ok(())
}

#[allow(non_snake_case)]
pub(crate) fn G(&self, n: usize) -> impl Iterator<Item = &RistrettoPoint> {
GensIter {
array: &self.G_vec,
n,
gen_idx: 0,
}
}

#[allow(non_snake_case)]
pub(crate) fn H(&self, n: usize) -> impl Iterator<Item = &RistrettoPoint> {
GensIter {
array: &self.H_vec,
n,
gen_idx: 0,
}
}
}

struct GensIter<'a> {
array: &'a Vec<RistrettoPoint>,
n: usize,
gen_idx: usize,
}

impl<'a> Iterator for GensIter<'a> {
type Item = &'a RistrettoPoint;

fn next(&mut self) -> Option<Self::Item> {
if self.gen_idx >= self.n {
None
} else {
let cur_gen = self.gen_idx;
self.gen_idx += 1;
Some(&self.array[cur_gen])
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.n - self.gen_idx;
(size, Some(size))
}
}
Loading
Loading