Skip to content
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/pcs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ flock-core = { workspace = true }
transcript = { workspace = true }
tracing = { workspace = true }
num-traits = { workspace = true }
poly = { workspace = true }

[dev-dependencies]
divan = { workspace = true }
Expand Down
39 changes: 1 addition & 38 deletions crates/pcs/src/challenger.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Flock challenger adapters over the project transcript.

use crate::bridge::{as_flock_f128, from_flock_f128};
use crate::pow::{find as find_pow, valid as pow_valid};
use field::F128 as LocalF128;
use flock_core::challenger::Challenger;
use flock_core::field::F128 as FlockF128;
Expand Down Expand Up @@ -232,44 +233,6 @@ impl Challenger for VerifierChallenger<'_, '_> {
}
}

/// todo: parallel pow? use potentially spongefish?
fn find_pow(seed: &[u8; 16], bits: u32) -> u64 {
if bits == 0 {
return 0;
}
let mut nonce = 0u64;
loop {
if pow_valid(seed, nonce, bits) {
return nonce;
}
nonce = nonce.checked_add(1).expect("proof-of-work nonce exhausted");
}
}

fn pow_valid(seed: &[u8; 16], nonce: u64, bits: u32) -> bool {
if bits == 0 {
return nonce == 0;
}
let mut hasher = blake3::Hasher::new();
hasher.update(b"bitz-pcs-pow-v1");
hasher.update(seed);
hasher.update(&nonce.to_le_bytes());
let digest = hasher.finalize();
leading_zero_bits(digest.as_bytes()) >= bits
}

fn leading_zero_bits(bytes: &[u8]) -> u32 {
let mut total = 0;
for byte in bytes {
let zeros = byte.leading_zeros();
total += zeros;
if zeros != 8 {
break;
}
}
total
}

#[cfg(test)]
mod tests {
use proptest::prelude::*;
Expand Down
68 changes: 65 additions & 3 deletions crates/pcs/src/commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@

use core::mem::size_of;

use crate::VerifyError;
use crate::bridge::as_flock_f128s;
use crate::ligerito::CheckedLigerito;
use crate::ood::{OodClaim, prove, verify};
use crate::profiles::{ood_grinding_bits, security_config};
use common::{Root, Shape};
use field::F128;
pub use flock_core::hash::HashKind;
use flock_core::pcs::Commitment as FlockCommitment;
use flock_core::pcs::ligerito::LigeritoProfile;
use flock_core::pcs::{PcsParams, ProverData as FlockProverData};
use transcript::Encoding;
use transcript::{Encoding, ProverState, VerifierState};

/// Errors from PCS configuration.
#[derive(Clone, Debug, PartialEq, Eq)]
Expand All @@ -38,14 +41,32 @@ pub enum CommitError {
pub struct Pcs {
params: PcsParams,
checked_ligerito: CheckedLigerito,
ood_grinding_bits: Option<u32>,
bit_len: usize,
packed_len: usize,
}

/// Flock state retained between commitment and openings.
/// OOD-aware commitment creation also retains the initial evaluation for each opening.
pub struct ProverData {
commitment: FlockCommitment,
flock_prover_data: FlockProverData,
pub(crate) ood: Option<OodClaim>,
}

/// Commitment and out-of-domain claim read from the verifier transcript.
/// The claim is authenticated only after [`CommitScheme::verify_lin_with_ood`](crate::CommitScheme::verify_lin_with_ood) succeeds.
#[derive(Debug)]
pub struct VerifierData {
pub(crate) root: Root,
pub(crate) ood: Option<OodClaim>,
}

impl VerifierData {
/// Returns the public commitment root.
pub fn root(&self) -> Root {
self.root
}
}

impl Pcs {
Expand All @@ -61,7 +82,7 @@ impl Pcs {
// The ladder fixes the L0 interleaving: the commit must use the same
// `log_batch_size` as the opening's `initial_k`, or the L0 tree is not
// reusable as Ligerito's first oracle.
let security = crate::profiles::security_config(m, security_profile, merkle_hash)?;
let security = security_config(m, security_profile, merkle_hash)?;
let params = PcsParams {
m,
log_inv_rate: security_profile.log_inv_rate(),
Expand All @@ -70,19 +91,22 @@ impl Pcs {
merkle_hash,
};
let checked_ligerito = CheckedLigerito::new(&params, &security)?;
let ood_grinding_bits = ood_grinding_bits(&security, checked_ligerito.log_n_u32() as usize);
let packed_len = 1usize
.checked_shl(checked_ligerito.log_n_u32())
.ok_or(ConfigError::Invalid("packed length overflow"))?;

Ok(Self {
params,
checked_ligerito,
ood_grinding_bits,
bit_len,
packed_len,
})
}

/// Commits to the exact configured number of packed field elements.
/// Commits to the packed codeword without sampling an OOD claim.
/// Use [`Self::commit_with_ood`] for protocols requiring initial OOD sampling.
#[tracing::instrument(name = "Commit witness", skip_all)]
pub fn commit(&self, packed_witness: &[F128]) -> Result<(Root, ProverData), CommitError> {
// 1. Input Validation
Expand All @@ -103,10 +127,44 @@ impl Pcs {
ProverData {
commitment: flock_commitment,
flock_prover_data,
ood: None,
},
))
}

/// Commits and retains the initial OOD claim for subsequent batched openings.
///
/// Call before witness-dependent challenges and continue with the same transcript.
/// [`CommitScheme::prove_lin`](crate::CommitScheme::prove_lin) batches the retained claim into each opening.
/// Profiles using unique decoding omit the OOD round.
///
/// Returns [`CommitError::PackedWitnessLengthMismatch`] before transcript mutation
/// if `packed_witness` does not have the configured length.
#[tracing::instrument(name = "Commit witness with OOD", skip_all)]
pub fn commit_with_ood(
&self,
packed_witness: &[F128],
transcript: &mut ProverState,
) -> Result<(Root, ProverData), CommitError> {
let (root, mut data) = self.commit(packed_witness)?;
data.ood = prove(self, &root.0, packed_witness, transcript);
Ok((root, data))
}

/// Receives the OOD claim for the public root before subsequent protocol challenges.
///
/// Mirrors [`Self::commit_with_ood`]. Invalid grinding or a truncated evaluation
/// returns [`VerifyError::MalformedProof`]; authentication of the evaluation is
/// deferred to [`CommitScheme::verify_lin_with_ood`](crate::CommitScheme::verify_lin_with_ood).
pub fn receive_commitment(
&self,
root: Root,
transcript: &mut VerifierState<'_>,
) -> Result<VerifierData, VerifyError> {
let ood = verify(self, &root.0, transcript)?;
Ok(VerifierData { root, ood })
}

pub fn bit_len(&self) -> usize {
self.bit_len
}
Expand All @@ -120,6 +178,10 @@ impl Pcs {
&self.params
}

pub(crate) fn ood_grinding_bits(&self) -> Option<u32> {
self.ood_grinding_bits
}

pub(crate) fn prover_config(&self) -> &flock_core::pcs::ligerito::ProverConfig {
self.checked_ligerito.prover_config()
}
Expand Down
77 changes: 73 additions & 4 deletions crates/pcs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
//! - [`Pcs`] stores trusted Flock parameters and the expected bit length.
//! - [`Root`] is the public Merkle root.
//! - [`ProverData`] retains the codeword and Merkle tree after commitment.
//! - [`VerifierData`] retains the root and OOD claim received before opening.
//! - [`OpeningQuery`] contains an MLE point and target, or a `common::LinearClaim<F128>`.
//! - [`CommitScheme`] connects commitment, proving, and verification to project transcripts.
//! - [`ConfigError`] reports configuration failures.
Expand All @@ -39,6 +40,9 @@
//! It consumes the packed witness and borrows [`ProverData`].
//! The caller must use matching transcript session and instance labels.
//! The caller must also call `VerifierState::check_eof` after successful verification.
//! Use [`Pcs::commit_with_ood`] and [`Pcs::receive_commitment`] before any
//! witness-dependent challenges to include the initial OOD claim. Proving batches
//! that retained claim automatically; verification uses [`Pcs::verify_lin_with_ood`].
//!
//! # Example
//!
Expand All @@ -65,8 +69,8 @@
//! target: F128::from(0u64),
//! };
//!
//! let (commitment, prover_data) = pcs.commit(&packed_witness).unwrap();
//! let mut prover = build_prover(b"pcs-example", b"zero-polynomial");
//! let (commitment, prover_data) = pcs.commit_with_ood(&packed_witness, &mut prover).unwrap();
//! pcs.prove_lin(
//! &prover_data,
//! packed_witness,
Expand All @@ -78,7 +82,8 @@
//! let proof = prover.finish();
//!
//! let mut verifier = build_verifier(b"pcs-example", b"zero-polynomial", &proof);
//! pcs.verify_lin(
//! let commitment = pcs.receive_commitment(commitment, &mut verifier).unwrap();
//! pcs.verify_lin_with_ood(
//! &commitment,
//! &query,
//! StatementBinding::Bind,
Expand All @@ -93,7 +98,9 @@ mod challenger;
mod commitment;
mod ligerito;
mod mle;
mod ood;
mod opening;
mod pow;
mod profiles;
mod sumcheck;
mod transpose;
Expand All @@ -105,7 +112,7 @@ mod transpose_tests;
use field::F128;
use transcript::{ProverState, VerifierState};

pub use commitment::{CommitError, ConfigError, HashKind, Pcs, ProverData};
pub use commitment::{CommitError, ConfigError, HashKind, Pcs, ProverData, VerifierData};
pub use common::{OpeningQuery, Root};
pub use flock_core::pcs::ligerito::LigeritoProfile;
pub use opening::{ProveError, VerifyError};
Expand Down Expand Up @@ -136,6 +143,8 @@ pub trait CommitScheme {
type Commitment;
/// Private data retained by the prover after commitment.
type ProverData;
/// Commitment and OOD claim retained by the verifier before opening.
type VerifierData;

/// Commits the caller-owned packed witness to `Enc_C(q_pkd)`, where
/// `q_pkd(y) = Σ_{v ∈ {0,1}^7} q(y, v) · basis[v]`.
Expand All @@ -145,9 +154,30 @@ pub trait CommitScheme {
packed_witness: &[F128],
) -> Result<(Self::Commitment, Self::ProverData), CommitError>;

/// Commits and retains the initial OOD claim for subsequent openings.
///
/// Call before witness-dependent challenges and continue with the same transcript.
/// Profiles without initial OOD sampling omit that round.
fn commit_with_ood(
&self,
packed_witness: &[F128],
transcript: &mut ProverState,
) -> Result<(Self::Commitment, Self::ProverData), CommitError>;

/// Receives the OOD claim for the public commitment before protocol challenges.
///
/// Mirrors [`Self::commit_with_ood`]. The returned claim must be authenticated
/// by [`Self::verify_lin_with_ood`] on the same transcript.
fn receive_commitment(
&self,
commitment: Self::Commitment,
transcript: &mut VerifierState<'_>,
) -> Result<Self::VerifierData, VerifyError>;

/// Consumes the exact packed witness and proves either opening query.
///
/// Inner-product claims first pass through quadratic sumcheck and then the MLE opening protocol.
/// An OOD claim retained by [`Self::commit_with_ood`] is batched into the opening.
fn prove_lin(
&self,
data: &Self::ProverData,
Expand All @@ -167,11 +197,24 @@ pub trait CommitScheme {
statement_binding: StatementBinding,
transcript: &mut VerifierState<'_>,
) -> Result<(), VerifyError>;

/// Verifies the linear query batched with the retained OOD claim.
///
/// Continue the transcript used by [`Self::receive_commitment`]. Borrowing
/// the retained state permits multiple openings against the same commitment.
fn verify_lin_with_ood(
&self,
commitment: &Self::VerifierData,
query: &OpeningQuery,
statement_binding: StatementBinding,
transcript: &mut VerifierState<'_>,
) -> Result<(), VerifyError>;
}

impl CommitScheme for Pcs {
type Commitment = Root;
type ProverData = ProverData;
type VerifierData = VerifierData;

fn commit(
&self,
Expand All @@ -180,6 +223,22 @@ impl CommitScheme for Pcs {
Pcs::commit(self, packed_witness)
}

fn commit_with_ood(
&self,
packed_witness: &[F128],
transcript: &mut ProverState,
) -> Result<(Self::Commitment, Self::ProverData), CommitError> {
Pcs::commit_with_ood(self, packed_witness, transcript)
}

fn receive_commitment(
&self,
commitment: Self::Commitment,
transcript: &mut VerifierState<'_>,
) -> Result<Self::VerifierData, VerifyError> {
Pcs::receive_commitment(self, commitment, transcript)
}

fn prove_lin(
&self,
data: &Self::ProverData,
Expand All @@ -205,6 +264,16 @@ impl CommitScheme for Pcs {
statement_binding: StatementBinding,
transcript: &mut VerifierState<'_>,
) -> Result<(), VerifyError> {
opening::verify(self, commitment, query, statement_binding, transcript)
opening::verify(self, commitment, query, statement_binding, None, transcript)
}

fn verify_lin_with_ood(
&self,
commitment: &Self::VerifierData,
query: &OpeningQuery,
statement_binding: StatementBinding,
transcript: &mut VerifierState<'_>,
) -> Result<(), VerifyError> {
opening::verify_lin_with_ood(self, commitment, query, statement_binding, transcript)
}
}
Loading
Loading