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

Adding zero-knowledge #293

Open
wants to merge 20 commits into
base: next
Choose a base branch
from
Open
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
18 changes: 15 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
# Changelog

## 0.11.0 (2024-11-24)
- [BREAKING] Made the prover generic over the `ConstraintCommitment` type (#343).

## 0.10.3 (2024-11-19) - `air`, `prover`, and `verifier` crates only
- Fixed partition size calculations in `PartitionOptions` (#340).

## 0.10.2 (2024-11-18)
- Implemented `core::error::Error` for error types (#341).

## 0.10.1 (2024-10-30)
- Fixed partition hashing and add logging to aux trace building (#338).

## 0.10.0 (2024-10-25)
- [BREAKING] Refactored maybe-async macro into simpler maybe-async and maybe-await macros (#283).
- [BREAKING] Introduce `VectorCommitment` abstraction (#285).
- [BREAKING] Introduced `VectorCommitment` abstraction (#285).
- Added `maybe-async-trait` procedural macro (#334).
- [BREAKING] Add options for partitioned trace commitments (#336).
- [BREAKING] Added options for partitioned trace commitments (#336).
- Updated minimum supported Rust version to 1.82.

## 0.9.3 (2024-09-25) - `utils/core` and `math` crates only
Expand All @@ -14,7 +26,7 @@
- Fixed `read_slice` impl for ReadAdapter` (#309).

## 0.9.1 (2024-06-24) - `utils/core` crate only
- Fixed `useize` serialization in `ByteWriter`.
- Fixed `usize` serialization in `ByteWriter`.

## 0.9.0 (2024-05-09)
- [BREAKING] Merged `TraceLayout` into `TraceInfo` (#245).
Expand Down
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<a href="https://github.com/novifinancial/winterfell/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
<img src="https://github.com/novifinancial/winterfell/workflows/CI/badge.svg?branch=main">
<a href="https://deps.rs/repo/github/novifinancial/winterfell"><img src="https://deps.rs/repo/github/novifinancial/winterfell/status.svg"></a>
<img src="https://img.shields.io/badge/prover-rustc_1.78+-lightgray.svg">
<img src="https://img.shields.io/badge/verifier-rustc_1.78+-lightgray.svg">
<img src="https://img.shields.io/badge/prover-rustc_1.82+-lightgray.svg">
<img src="https://img.shields.io/badge/verifier-rustc_1.82+-lightgray.svg">
<a href="https://crates.io/crates/winterfell"><img src="https://img.shields.io/crates/v/winterfell"></a>

A STARK prover and verifier for arbitrary computations.
Expand Down Expand Up @@ -270,6 +270,8 @@ impl Prover for WorkProver {
type TraceLde<E: FieldElement<BaseField = BaseElement>> = DefaultTraceLde<E, Blake3>;
type ConstraintEvaluator<'a, E: FieldElement<BaseField = BaseElement>> =
DefaultConstraintEvaluator<'a, WorkAir, E>;
type ConstraintCommitment<E: FieldElement<BaseField = Self::BaseField>> =
DefaultConstraintCommitment<E, H, Self::VC>;

// Our public inputs consist of the first and last value in the execution trace.
fn get_pub_inputs(&self, trace: &Self::Trace) -> PublicInputs {
Expand Down Expand Up @@ -300,6 +302,22 @@ impl Prover for WorkProver {
DefaultConstraintEvaluator::new(air, aux_rand_elements, composition_coefficients)
}

// We'll use the default constraint commitment.
fn build_constraint_commitment<E: FieldElement<BaseField = Self::BaseField>>(
&self,
composition_poly_trace: CompositionPolyTrace<E>,
num_constraint_composition_columns: usize,
domain: &StarkDomain<Self::BaseField>,
partition_options: PartitionOptions,
) -> (Self::ConstraintCommitment<E>, CompositionPoly<E>) {
DefaultConstraintCommitment::new(
composition_poly_trace,
num_constraint_composition_columns,
domain,
partition_options,
)
}

fn options(&self) -> &ProofOptions {
&self.options
}
Expand Down
14 changes: 7 additions & 7 deletions air/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[package]
name = "winter-air"
version = "0.10.0"
version = "0.11.0"
description = "AIR components for the Winterfell STARK prover/verifier"
authors = ["winterfell contributors"]
readme = "README.md"
license = "MIT"
repository = "https://github.com/novifinancial/winterfell"
documentation = "https://docs.rs/winter-air/0.10.0"
documentation = "https://docs.rs/winter-air/0.11.0"
categories = ["cryptography", "no-std"]
keywords = ["crypto", "arithmetization", "air"]
edition = "2021"
Expand All @@ -20,14 +20,14 @@ default = ["std"]
std = ["crypto/std", "fri/std", "math/std", "utils/std"]

[dependencies]
crypto = { version = "0.10", path = "../crypto", package = "winter-crypto", default-features = false }
fri = { version = "0.10", path = "../fri", package = "winter-fri", default-features = false }
crypto = { version = "0.11", path = "../crypto", package = "winter-crypto", default-features = false }
fri = { version = "0.11", path = "../fri", package = "winter-fri", default-features = false }
libm = "0.2"
math = { version = "0.10", path = "../math", package = "winter-math", default-features = false }
utils = { version = "0.10", path = "../utils/core", package = "winter-utils", default-features = false }
math = { version = "0.11", path = "../math", package = "winter-math", default-features = false }
utils = { version = "0.11", path = "../utils/core", package = "winter-utils", default-features = false }

[dev-dependencies]
rand-utils = { version = "0.10", path = "../utils/rand", package = "winter-rand-utils" }
rand-utils = { version = "0.11", path = "../utils/rand", package = "winter-rand-utils" }

# Allow math in docs
[package.metadata.docs.rs]
Expand Down
2 changes: 1 addition & 1 deletion air/src/air/boundary/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ where
let group = groups.entry(key).or_insert_with(|| {
BoundaryConstraintGroup::new(ConstraintDivisor::from_assertion(
&assertion,
context.trace_len(),
context.trace_info().length(),
))
});

Expand Down
161 changes: 142 additions & 19 deletions air/src/air/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub struct AirContext<B: StarkField> {
pub(super) trace_domain_generator: B,
pub(super) lde_domain_generator: B,
pub(super) num_transition_exemptions: usize,
pub(super) trace_length_ext: usize,
pub(super) zk_parameters: Option<ZkParameters>,
}

impl<B: StarkField> AirContext<B> {
Expand Down Expand Up @@ -133,18 +135,35 @@ impl<B: StarkField> AirContext<B> {
);
}

let h = options.zk_witness_randomizer_degree().unwrap_or(0);
let trace_length = trace_info.length();
let trace_length_ext = (trace_length + h as usize).next_power_of_two();
let zk_blowup = trace_length_ext / trace_length;
let lde_domain_size = trace_length_ext * options.blowup_factor();
// equation (12) in https://eprint.iacr.org/2024/1037
let h_q = options.num_queries() + 1;
let zk_parameters = if options.is_zk() {
Some(ZkParameters {
degree_witness_randomizer: h as usize,
degree_constraint_randomizer: h_q,
zk_blowup_witness: zk_blowup,
})
} else {
None
};

// determine minimum blowup factor needed to evaluate transition constraints by taking
// the blowup factor of the highest degree constraint
let mut ce_blowup_factor = 0;
for degree in main_transition_constraint_degrees.iter() {
if degree.min_blowup_factor() > ce_blowup_factor {
ce_blowup_factor = degree.min_blowup_factor();
if degree.min_blowup_factor(trace_length, trace_length_ext) > ce_blowup_factor {
ce_blowup_factor = degree.min_blowup_factor(trace_length, trace_length_ext);
}
}

for degree in aux_transition_constraint_degrees.iter() {
if degree.min_blowup_factor() > ce_blowup_factor {
ce_blowup_factor = degree.min_blowup_factor();
if degree.min_blowup_factor(trace_length, trace_length_ext) > ce_blowup_factor {
ce_blowup_factor = degree.min_blowup_factor(trace_length, trace_length_ext);
}
}

Expand All @@ -155,9 +174,6 @@ impl<B: StarkField> AirContext<B> {
options.blowup_factor()
);

let trace_length = trace_info.length();
let lde_domain_size = trace_length * options.blowup_factor();

AirContext {
options,
trace_info,
Expand All @@ -170,6 +186,8 @@ impl<B: StarkField> AirContext<B> {
trace_domain_generator: B::get_root_of_unity(trace_length.ilog2()),
lde_domain_generator: B::get_root_of_unity(lde_domain_size.ilog2()),
num_transition_exemptions: 1,
trace_length_ext,
zk_parameters,
}
}

Expand All @@ -188,25 +206,31 @@ impl<B: StarkField> AirContext<B> {
self.trace_info.length()
}

/// Returns length of the possibly extended execution trace. This is the same as the original
/// trace length when zero-knowledge is not enabled.
pub fn trace_length_ext(&self) -> usize {
self.trace_length_ext
}

/// Returns degree of trace polynomials for an instance of a computation.
///
/// The degree is always `trace_length` - 1.
/// The degree is always `trace_length_ext` - 1.
pub fn trace_poly_degree(&self) -> usize {
self.trace_info.length() - 1
self.trace_length_ext() - 1
}

/// Returns size of the constraint evaluation domain.
///
/// This is guaranteed to be a power of two, and is equal to `trace_length * ce_blowup_factor`.
/// This is guaranteed to be a power of two, and is equal to `trace_length_ext * ce_blowup_factor`.
pub fn ce_domain_size(&self) -> usize {
self.trace_info.length() * self.ce_blowup_factor
self.trace_length_ext() * self.ce_blowup_factor
}

/// Returns the size of the low-degree extension domain.
///
/// This is guaranteed to be a power of two, and is equal to `trace_length * lde_blowup_factor`.
/// This is guaranteed to be a power of two, and is equal to `trace_length_ext * lde_blowup_factor`.
pub fn lde_domain_size(&self) -> usize {
self.trace_info.length() * self.options.blowup_factor()
self.trace_length_ext() * self.options.blowup_factor()
}

/// Returns the number of transition constraints for a computation, excluding the Lagrange
Expand Down Expand Up @@ -292,26 +316,102 @@ impl<B: StarkField> AirContext<B> {
/// numerator is `trace_len - 1` for all transition constraints (i.e. the base degree is 1).
/// Hence, no matter what the degree of the divisor is for each, the degree of the fraction will
/// be at most `trace_len - 1`.
///
/// TODO: update documentation
pub fn num_constraint_composition_columns(&self) -> usize {
let mut highest_constraint_degree = 0_usize;
for degree in self
.main_transition_constraint_degrees
.iter()
.chain(self.aux_transition_constraint_degrees.iter())
{
let eval_degree = degree.get_evaluation_degree(self.trace_len());
let eval_degree =
degree.get_evaluation_degree(self.trace_len(), self.trace_length_ext());
if eval_degree > highest_constraint_degree {
highest_constraint_degree = eval_degree
}
}
let trace_length = self.trace_len();
let trace_length_ext = self.trace_length_ext();
let transition_divisior_degree = trace_length - self.num_transition_exemptions();

// we use the identity: ceil(a/b) = (a + b - 1)/b
let num_constraint_col =
(highest_constraint_degree - transition_divisior_degree).div_ceil(trace_length);
(highest_constraint_degree - transition_divisior_degree).div_ceil(trace_length_ext);

if self.zk_parameters.is_some() {
let quotient_degree = if highest_constraint_degree < trace_length_ext {
// This means that our transition constraints have degree 1 and hence the boundary
// constraints will determine the degree
trace_length_ext - 2
} else {
highest_constraint_degree - transition_divisior_degree
};
let n_q = self.options.num_queries();
let den = self.trace_length_ext() - (n_q + 1);

(quotient_degree + 1).div_ceil(den)
} else {
cmp::max(num_constraint_col, 1)
}
}

pub fn constraint_composition_degree(&self) -> usize {
let mut highest_constraint_degree = 0_usize;
for degree in self
.main_transition_constraint_degrees
.iter()
.chain(self.aux_transition_constraint_degrees.iter())
{
let eval_degree =
degree.get_evaluation_degree(self.trace_len(), self.trace_length_ext());
if eval_degree > highest_constraint_degree {
highest_constraint_degree = eval_degree
}
}
let trace_length = self.trace_len();
let transition_divisior_degree = trace_length - self.num_transition_exemptions();

cmp::max(num_constraint_col, 1)
// highest_constraint_degree - transition_divisior_degree
if highest_constraint_degree < self.trace_length_ext {
// This means that our transition constraints have degree 1 and hence the boundary
// constraints will determine the degree
self.trace_length_ext - 2
} else {
highest_constraint_degree - transition_divisior_degree
}
}

pub fn num_coefficients_chunk_quotient(&self) -> usize {
if self.zk_parameters().is_some() {
let num_constraint_composition_cols = self.num_constraint_composition_columns();
let quotient_degree = self.constraint_composition_degree();

(quotient_degree + 1).div_ceil(num_constraint_composition_cols)
} else {
self.trace_len()
}
}

pub fn zk_parameters(&self) -> Option<ZkParameters> {
self.zk_parameters
}

pub fn zk_blowup_factor(&self) -> usize {
self.zk_parameters()
.map(|parameters| parameters.zk_blowup_witness())
.unwrap_or(1)
}

pub fn zk_witness_randomizer_degree(&self) -> usize {
self.zk_parameters()
.map(|parameters| parameters.degree_witness_randomizer())
.unwrap_or(0)
}

pub fn zk_constraint_randomizer_degree(&self) -> usize {
self.zk_parameters()
.map(|parameters| parameters.degree_constraint_randomizer())
.unwrap_or(0)
}

// DATA MUTATORS
Expand Down Expand Up @@ -347,9 +447,11 @@ impl<B: StarkField> AirContext<B> {
.iter()
.chain(self.aux_transition_constraint_degrees.iter())
{
let eval_degree = degree.get_evaluation_degree(self.trace_len());
let eval_degree =
degree.get_evaluation_degree(self.trace_len(), self.trace_length_ext());
let max_constraint_composition_degree = self.ce_domain_size() - 1;
let max_exemptions = max_constraint_composition_degree + self.trace_len() - eval_degree;
let max_exemptions =
max_constraint_composition_degree + self.trace_length_ext() - eval_degree;
assert!(
n <= max_exemptions,
"number of transition exemptions cannot exceed: {max_exemptions}, but was {n}"
Expand All @@ -360,3 +462,24 @@ impl<B: StarkField> AirContext<B> {
self
}
}

#[derive(Clone, Copy, PartialEq, Eq)]
pub struct ZkParameters {
degree_witness_randomizer: usize,
degree_constraint_randomizer: usize,
zk_blowup_witness: usize,
}

impl ZkParameters {
pub fn degree_witness_randomizer(&self) -> usize {
self.degree_witness_randomizer
}

pub fn degree_constraint_randomizer(&self) -> usize {
self.degree_constraint_randomizer
}

pub fn zk_blowup_witness(&self) -> usize {
self.zk_blowup_witness
}
}
Loading