Skip to content
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
6 changes: 3 additions & 3 deletions crates/examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ base64.workspace = true
binius-circuits = { path = "../circuits" }
binius-core = { path = "../core" }
binius-frontend = { path = "../frontend" }
binius-hash = { path = "../hash", default-features = false }
binius-hash = { path = "../hash" }
binius-prover = { path = "../prover", default-features = false }
binius-transcript = { path = "../transcript" }
binius-utils = { path = "../utils", features = ["platform-diagnostics"] }
binius-verifier = { path = "../verifier", default-features = false }
binius-verifier = { path = "../verifier" }
bitcoin_hashes.workspace = true
bitcoin.workspace = true
blake2.workspace = true
Expand Down Expand Up @@ -70,7 +70,7 @@ harness = false
[features]
default = ["rayon"]
perfetto = ["tracing-profile/perfetto"]
rayon = ["binius-hash/rayon", "binius-prover/rayon", "binius-verifier/rayon"]
rayon = ["binius-hash/rayon", "binius-prover/rayon"]

# Tutorial examples
[[example]]
Expand Down
2 changes: 1 addition & 1 deletion crates/hash/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ tracing.workspace = true
rand.workspace = true

[features]
default = ["rayon"]
default = []
rayon = ["binius-utils/rayon"]
6 changes: 3 additions & 3 deletions crates/iop-prover/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ workspace = true

[dependencies]
binius-field = { path = "../field" }
binius-hash = { path = "../hash", default-features = false }
binius-iop = { path = "../iop", default-features = false }
binius-hash = { path = "../hash" }
binius-iop = { path = "../iop" }
binius-ip = { path = "../ip" }
binius-ip-prover = { path = "../ip-prover", default-features = false }
binius-math = { path = "../math" }
Expand All @@ -34,4 +34,4 @@ sha2.workspace = true

[features]
default = ["rayon"]
rayon = ["binius-utils/rayon", "binius-hash/rayon", "binius-iop/rayon", "binius-ip-prover/rayon"]
rayon = ["binius-utils/rayon"]
6 changes: 1 addition & 5 deletions crates/iop/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ workspace = true

[dependencies]
binius-field = { path = "../field" }
binius-hash = { path = "../hash", default-features = false }
binius-hash = { path = "../hash" }
binius-ip = { path = "../ip" }
binius-math = { path = "../math" }
binius-transcript = { path = "../transcript" }
Expand All @@ -25,7 +25,3 @@ tracing.workspace = true
binius-math = { path = "../math", features = ["test-utils"] }
rand.workspace = true
sha2.workspace = true

[features]
default = ["rayon"]
rayon = ["binius-utils/rayon", "binius-hash/rayon"]
10 changes: 7 additions & 3 deletions crates/prover/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ workspace = true
[dependencies]
binius-core = { path = "../core" }
binius-field = { path = "../field" }
binius-hash = { path = "../hash", default-features = false }
binius-hash = { path = "../hash" }
binius-iop = { path = "../iop" }
binius-iop-prover = { path = "../iop-prover", default-features = false }
binius-ip-prover = { path = "../ip-prover", default-features = false }
binius-math = { path = "../math" }
binius-spartan-frontend = { path = "../spartan-frontend" }
binius-spartan-prover = { path = "../spartan-prover", default-features = false }
binius-spartan-verifier = { path = "../spartan-verifier" }
binius-transcript = { path = "../transcript" }
binius-verifier = { path = "../verifier", default-features = false }
binius-verifier = { path = "../verifier" }
binius-utils = { path = "../utils" }
bytemuck.workspace = true
bytes.workspace = true
Expand Down Expand Up @@ -87,4 +91,4 @@ harness = false

[features]
default = ["rayon"]
rayon = ["binius-utils/rayon", "binius-hash/rayon", "binius-iop-prover/rayon", "binius-ip-prover/rayon"]
rayon = ["binius-utils/rayon"]
2 changes: 2 additions & 0 deletions crates/prover/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
//!
//! - [`Prover`] - Main proving interface; call [`Prover::setup`] with a verifier, then
//! [`Prover::prove`] with witness data
//! - [`IOPProver`] - Core IOP proving logic, independent of the compilation strategy
//! - [`KeyCollection`] - Precomputed keys for shift reduction (can be serialized for reuse)
//!
//! # Related crates
Expand All @@ -33,6 +34,7 @@ pub mod fold_word;
pub mod protocols;
mod prove;
pub mod ring_switch;
pub mod zk_config;

pub use binius_field::arch::OptimalPackedB128;
pub use binius_hash as hash;
Expand Down
229 changes: 140 additions & 89 deletions crates/prover/src/prove.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2025 Irreducible Inc.

use binius_core::{
constraint_system::{AndConstraint, MulConstraint, ValueVec},
constraint_system::{AndConstraint, ConstraintSystem, MulConstraint, ValueVec},
verify::eval_operand,
word::Word,
};
Expand All @@ -23,7 +23,7 @@ use binius_math::{
use binius_transcript::{ProverTranscript, fiat_shamir::Challenger};
use binius_utils::{SerializeBytes, checked_arithmetics::checked_log_2, rayon::prelude::*};
use binius_verifier::{
Verifier,
IOPVerifier, Verifier,
and_reduction::verifier::AndCheckOutput,
config::{
B1, B128, LOG_WORD_SIZE_BITS, LOG_WORDS_PER_ELEM, PROVER_SMALL_FIELD_ZEROCHECK_CHALLENGES,
Expand Down Expand Up @@ -53,84 +53,36 @@ type ProverNTT<F> = NeighborsLastMultiThread<GenericPreExpanded<F>>;
type ProverMerkleProver<F, ParallelMerkleHasher, ParallelMerkleCompress> =
BinaryMerkleTreeProver<F, ParallelMerkleHasher, ParallelMerkleCompress>;

/// Struct for proving instances of a particular constraint system.
/// IOP prover for a particular constraint system.
///
/// The [`Self::setup`] constructor pre-processes reusable structures for proving instances of the
/// given constraint system. Then [`Self::prove`] is called one or more times with individual
/// instances.
/// This struct encapsulates the constraint system and pre-computed keys,
/// providing the core proving logic independent of the specific IOP compilation strategy.
/// Most users should use [`Prover`] instead, which wraps this with a BaseFold compiler.
#[derive(Debug)]
pub struct Prover<P, ParallelMerkleCompress, ParallelMerkleHasher>
where
P: PackedField<Scalar = B128>,
ParallelMerkleHasher: ParallelDigest,
ParallelMerkleHasher::Digest: Digest + BlockSizeUser + FixedOutputReset,
ParallelMerkleCompress: ParallelPseudoCompression<Output<ParallelMerkleHasher::Digest>, 2>,
{
pub struct IOPProver {
constraint_system: ConstraintSystem,
log_public_words: usize,
log_witness_elems: usize,
key_collection: KeyCollection,
verifier: Verifier<ParallelMerkleHasher::Digest, ParallelMerkleCompress::Compression>,
#[allow(clippy::type_complexity)]
basefold_compiler: BaseFoldProverCompiler<
P,
ProverNTT<B128>,
ProverMerkleProver<B128, ParallelMerkleHasher, ParallelMerkleCompress>,
>,
}

impl<P, MerkleHash, ParallelMerkleCompress, ParallelMerkleHasher>
Prover<P, ParallelMerkleCompress, ParallelMerkleHasher>
where
P: PackedField<Scalar = B128>
+ PackedExtension<B128>
+ PackedExtension<B1>
+ WithUnderlier<Underlier: UnderlierWithBitOps>,
MerkleHash: Digest + BlockSizeUser + FixedOutputReset,
ParallelMerkleHasher: ParallelDigest<Digest = MerkleHash>,
ParallelMerkleCompress: ParallelPseudoCompression<Output<MerkleHash>, 2>,
Output<MerkleHash>: SerializeBytes,
{
/// Constructs a prover corresponding to a constraint system verifier.
///
/// See [`Prover`] struct documentation for details.
pub fn setup(
verifier: Verifier<MerkleHash, ParallelMerkleCompress::Compression>,
compression: ParallelMerkleCompress,
) -> Result<Self, Error> {
let key_collection = build_key_collection(verifier.constraint_system());
Self::setup_with_key_collection(verifier, compression, key_collection)
impl IOPProver {
/// Constructs an IOP prover from an IOP verifier and pre-computed keys.
pub fn new(iop_verifier: IOPVerifier, key_collection: KeyCollection) -> Self {
let log_public_words = iop_verifier.log_public_words();
let log_witness_elems = iop_verifier.log_witness_elems();
let constraint_system = iop_verifier.into_constraint_system();
Self {
constraint_system,
log_public_words,
log_witness_elems,
key_collection,
}
}

/// Constructs a prover with a pre-built KeyCollection.
///
/// This allows loading a previously serialized KeyCollection to avoid
/// the expensive key building phase during setup.
pub fn setup_with_key_collection(
verifier: Verifier<MerkleHash, ParallelMerkleCompress::Compression>,
compression: ParallelMerkleCompress,
key_collection: KeyCollection,
) -> Result<Self, Error> {
// Get max subspace from verifier's IOP compiler (reuses FRI params)
let subspace = verifier.iop_compiler().max_subspace();
let domain_context = GenericPreExpanded::generate_from_subspace(subspace);
// FIXME TODO For mobile phones, the number of shares should potentially be more than the
// number of threads, because the threads/cores have different performance (but in the NTT
// each share has the same amount of work)
let log_num_shares = binius_utils::rayon::current_num_threads().ilog2() as usize;
let ntt = NeighborsLastMultiThread::new(domain_context, log_num_shares);

let merkle_prover = BinaryMerkleTreeProver::<_, ParallelMerkleHasher, _>::new(compression);

// Create prover compiler from verifier compiler (reuses FRI params and oracle specs)
let basefold_compiler = BaseFoldProverCompiler::from_verifier_compiler(
verifier.iop_compiler(),
ntt,
merkle_prover,
);

Ok(Prover {
key_collection,
verifier,
basefold_compiler,
})
/// Returns the constraint system.
pub fn constraint_system(&self) -> &ConstraintSystem {
&self.constraint_system
}

/// Returns a reference to the KeyCollection.
Expand All @@ -140,22 +92,19 @@ where
&self.key_collection
}

pub fn prove<Challenger_: Challenger>(
&self,
witness: ValueVec,
transcript: &mut ProverTranscript<Challenger_>,
) -> Result<(), Error> {
// Create channel and delegate to prove_iop
let channel = BaseFoldProverChannel::from_compiler(&self.basefold_compiler, transcript);
self.prove_iop(witness, channel)
}

fn prove_iop<Channel>(&self, witness: ValueVec, mut channel: Channel) -> Result<(), Error>
/// Proves using an IOP channel interface.
///
/// This is the core proving logic, independent of the specific IOP compilation strategy.
/// For most users, [`Prover::prove`] is the simpler interface.
pub fn prove<P, Channel>(&self, witness: ValueVec, mut channel: Channel) -> Result<(), Error>
where
P: PackedField<Scalar = B128>
+ PackedExtension<B128>
+ PackedExtension<B1>
+ WithUnderlier<Underlier: UnderlierWithBitOps>,
Channel: IOPProverChannel<P>,
{
let verifier = &self.verifier;
let cs = self.verifier.constraint_system();
let cs = &self.constraint_system;

let _prove_guard = tracing::info_span!(
"Prove",
Expand All @@ -171,11 +120,11 @@ where
let setup_guard =
tracing::info_span!("[phase] Setup", phase = "setup", perfetto_category = "phase")
.entered();
let witness_packed = pack_witness::<P>(verifier.log_witness_elems(), &witness)?;
let witness_packed = pack_witness::<P>(self.log_witness_elems, &witness)?;
drop(setup_guard);

// Observe the public input as B128 elements (includes it in Fiat-Shamir).
let n_public_elems = 1 << (verifier.log_public_words() - LOG_WORDS_PER_ELEM);
let n_public_elems = 1 << (self.log_public_words - LOG_WORDS_PER_ELEM);
let public_elems = witness_packed
.iter_scalars()
.take(n_public_elems)
Expand Down Expand Up @@ -296,7 +245,7 @@ where
// Public input check batched with ring-switch
let log_packing = <B128 as ExtensionField<B1>>::LOG_DEGREE;

let log_public_elems = verifier.log_public_words() - LOG_WORDS_PER_ELEM;
let log_public_elems = self.log_public_words - LOG_WORDS_PER_ELEM;
let pubcheck_point = &eval_point[log_packing..][..log_public_elems];
let pubcheck_claim = {
let public_elems_buf = FieldSlice::from_slice(log_public_elems, &public_elems);
Expand All @@ -319,6 +268,108 @@ where
}
}

/// Struct for proving instances of a particular constraint system.
///
/// The [`Self::setup`] constructor pre-processes reusable structures for proving instances of the
/// given constraint system. Then [`Self::prove`] is called one or more times with individual
/// instances.
pub struct Prover<P, ParallelMerkleCompress, ParallelMerkleHasher>
where
P: PackedField<Scalar = B128>,
ParallelMerkleHasher: ParallelDigest,
ParallelMerkleHasher::Digest: Digest + BlockSizeUser + FixedOutputReset,
ParallelMerkleCompress: ParallelPseudoCompression<Output<ParallelMerkleHasher::Digest>, 2>,
{
iop_prover: IOPProver,
#[allow(clippy::type_complexity)]
basefold_compiler: BaseFoldProverCompiler<
P,
ProverNTT<B128>,
ProverMerkleProver<B128, ParallelMerkleHasher, ParallelMerkleCompress>,
>,
}

impl<P, MerkleHash, ParallelMerkleCompress, ParallelMerkleHasher>
Prover<P, ParallelMerkleCompress, ParallelMerkleHasher>
where
P: PackedField<Scalar = B128>
+ PackedExtension<B128>
+ PackedExtension<B1>
+ WithUnderlier<Underlier: UnderlierWithBitOps>,
MerkleHash: Digest + BlockSizeUser + FixedOutputReset,
ParallelMerkleHasher: ParallelDigest<Digest = MerkleHash>,
ParallelMerkleCompress: ParallelPseudoCompression<Output<MerkleHash>, 2>,
Output<MerkleHash>: SerializeBytes,
{
/// Constructs a prover corresponding to a constraint system verifier.
///
/// See [`Prover`] struct documentation for details.
pub fn setup(
verifier: Verifier<MerkleHash, ParallelMerkleCompress::Compression>,
compression: ParallelMerkleCompress,
) -> Result<Self, Error> {
let key_collection = build_key_collection(verifier.constraint_system());
Self::setup_with_key_collection(verifier, compression, key_collection)
}

/// Constructs a prover with a pre-built KeyCollection.
///
/// This allows loading a previously serialized KeyCollection to avoid
/// the expensive key building phase during setup.
pub fn setup_with_key_collection(
verifier: Verifier<MerkleHash, ParallelMerkleCompress::Compression>,
compression: ParallelMerkleCompress,
key_collection: KeyCollection,
) -> Result<Self, Error> {
// Get max subspace from verifier's IOP compiler (reuses FRI params)
let subspace = verifier.iop_compiler().max_subspace();
let domain_context = GenericPreExpanded::generate_from_subspace(subspace);
// FIXME TODO For mobile phones, the number of shares should potentially be more than the
// number of threads, because the threads/cores have different performance (but in the NTT
// each share has the same amount of work)
let log_num_shares = binius_utils::rayon::current_num_threads().ilog2() as usize;
let ntt = NeighborsLastMultiThread::new(domain_context, log_num_shares);

let merkle_prover = BinaryMerkleTreeProver::<_, ParallelMerkleHasher, _>::new(compression);

// Create prover compiler from verifier compiler (reuses FRI params and oracle specs)
let basefold_compiler = BaseFoldProverCompiler::from_verifier_compiler(
verifier.iop_compiler(),
ntt,
merkle_prover,
);

let iop_prover = IOPProver::new(verifier.into_iop_verifier(), key_collection);

Ok(Prover {
iop_prover,
basefold_compiler,
})
}

/// Returns a reference to the IOP prover.
pub fn iop_prover(&self) -> &IOPProver {
&self.iop_prover
}

/// Returns a reference to the KeyCollection.
///
/// This can be used to serialize the KeyCollection for later use.
pub fn key_collection(&self) -> &KeyCollection {
self.iop_prover.key_collection()
}

pub fn prove<Challenger_: Challenger>(
&self,
witness: ValueVec,
transcript: &mut ProverTranscript<Challenger_>,
) -> Result<(), Error> {
// Create channel and delegate to IOPProver::prove
let channel = BaseFoldProverChannel::from_compiler(&self.basefold_compiler, transcript);
self.iop_prover.prove::<P, _>(witness, channel)
}
}

/// Batches the pubcheck transparent polynomial with the ring-switch equality indicator.
///
/// Computes `rs_eq_ind + batch_coeff * eq(pubcheck_point || 0, ·)`, adding the scaled
Expand Down
Loading
Loading