Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ade3b23
feat(zkvm): integrate precommitted Dory geometry across prover and ve…
RadNi Mar 14, 2026
07b55f5
fix(claim-reduction): preserve advice handoff scale
omibo Apr 15, 2026
ad3e8a7
fix(advice): align verifier scale with precommit order
quangvdao May 12, 2026
57cba2a
fix(zkvm): align precommitted advice with stage6 split
RadNi May 20, 2026
0bc845e
fix(zkvm): update transpilable verifier for precommitted advice
RadNi May 20, 2026
41bff73
fix(zkvm): transition precommitted advice in transpilable verifier
RadNi May 20, 2026
589fe61
style: format precommitted advice branch
RadNi May 20, 2026
d1c27ec
fix(zkvm): satisfy strict clippy for precommitted advice
RadNi May 20, 2026
d641b5d
fix(zkvm): remove unused advice output constraint helper
RadNi May 20, 2026
5b4c166
refactor(zkvm): align precommitted advice reduction shape
RadNi May 20, 2026
8353ada
refactor(dory): move address-major layout cleanup into precommitted s…
RadNi May 20, 2026
b3c62bb
refactor(dory): align commitment scheme before committed programs
RadNi May 20, 2026
a169a45
chore(dory): remove unused opening hint accessors
RadNi May 20, 2026
b388d1a
fix(dory): derive stage8 opening point by layout
RadNi May 20, 2026
98dfdd0
fix(dory): match final stage8 verifier error
RadNi May 20, 2026
9b37aef
chore(dory): remove dense tier setup alias
RadNi May 22, 2026
eeb2613
refactor(dory): share stage8 opening point selection
RadNi May 22, 2026
2fea03c
fix(zkvm): preserve precommitted cycle openings
RadNi May 23, 2026
0a70e07
fix(zkvm): gate final opening helper behind prover
RadNi May 25, 2026
b5832c0
fix(zkvm): share final opening helper
RadNi May 25, 2026
c41f679
fix(zkvm): drop stale opening point imports
RadNi May 25, 2026
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
50 changes: 19 additions & 31 deletions jolt-core/src/poly/commitment/dory/commitment_scheme.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Dory polynomial commitment scheme implementation

use super::dory_globals::{DoryGlobals, DoryLayout};
use super::dory_globals::DoryGlobals;
use super::jolt_dory_routines::{JoltG1Routines, JoltG2Routines};
use super::wrappers::{
ark_to_jolt, jolt_to_ark, ArkDoryProof, ArkFr, ArkG1, ArkGT, ArkworksProverSetup,
Expand Down Expand Up @@ -63,6 +63,18 @@ fn maybe_blind_commitment(setup: &ArkworksProverSetup, commitment: ArkGT) -> (Ar
}
}

#[inline]
fn canonical_setup_log_n(max_num_vars: usize) -> usize {
// Dory's generator count depends on ceil(max_log_n / 2), so odd/even pairs like
// 23 and 24 share the same generator bucket. Canonicalizing to the even bucket
// representative keeps those runs on a single URS file.
if max_num_vars.is_multiple_of(2) {
max_num_vars
} else {
max_num_vars + 1
}
}

pub fn bind_opening_inputs<F: JoltField, ProofTranscript: Transcript>(
transcript: &mut ProofTranscript,
opening_point: &[F::Challenge],
Expand Down Expand Up @@ -105,13 +117,13 @@ impl CommitmentScheme for DoryCommitmentScheme {

fn setup_prover(max_num_vars: usize) -> Self::ProverSetup {
let _span = trace_span!("DoryCommitmentScheme::setup_prover").entered();
let canonical_max_num_vars = canonical_setup_log_n(max_num_vars);
#[cfg(test)]
DoryGlobals::configure_test_cache_root();

#[cfg(not(target_arch = "wasm32"))]
let setup = ArkworksProverSetup::new_from_urs(max_num_vars);
let setup = ArkworksProverSetup::new_from_urs(canonical_max_num_vars);
#[cfg(target_arch = "wasm32")]
let setup = ArkworksProverSetup::new(max_num_vars);
let setup = ArkworksProverSetup::new(canonical_max_num_vars);

// The prepared-point cache in dory-pcs is global and can only be initialized once.
// In unit tests, multiple setups with different sizes are created, so initializing the
Expand Down Expand Up @@ -194,8 +206,7 @@ impl CommitmentScheme for DoryCommitmentScheme {
let sigma = num_cols.log_2();
let nu = num_rows.log_2();

let reordered_point = reorder_opening_point_for_layout::<ark_bn254::Fr>(opening_point);
let ark_point: Vec<ArkFr> = reordered_point
let ark_point: Vec<ArkFr> = opening_point
.iter()
.rev()
.map(|p| {
Expand Down Expand Up @@ -237,10 +248,8 @@ impl CommitmentScheme for DoryCommitmentScheme {
) -> Result<(), ProofVerifyError> {
let _span = trace_span!("DoryCommitmentScheme::verify").entered();

let reordered_point = reorder_opening_point_for_layout::<ark_bn254::Fr>(opening_point);

// Dory uses the opposite endian-ness as Jolt
let ark_point: Vec<ArkFr> = reordered_point
let ark_point: Vec<ArkFr> = opening_point
.iter()
.rev()
.map(|p| {
Expand Down Expand Up @@ -270,7 +279,7 @@ impl CommitmentScheme for DoryCommitmentScheme {
setup.clone().into_inner(),
&mut dory_transcript,
)
.map_err(|_| ProofVerifyError::InternalError)?;
.map_err(|err| ProofVerifyError::DoryError(format!("dory::verify failed: {err:?}")))?;

Ok(())
}
Expand Down Expand Up @@ -496,24 +505,3 @@ where
Some((g1s, h1))
}
}

/// Reorders opening_point for AddressMajor layout.
///
/// For AddressMajor layout, reorders opening_point from [r_address, r_cycle] to [r_cycle, r_address].
/// This ensures that after Dory's reversal and splitting:
/// - Column (right) vector gets address variables (matching AddressMajor column indexing)
/// - Row (left) vector gets cycle variables (matching AddressMajor row indexing)
///
/// For CycleMajor layout, returns the point unchanged.
fn reorder_opening_point_for_layout<F: JoltField>(
opening_point: &[F::Challenge],
) -> Vec<F::Challenge> {
if DoryGlobals::get_layout() == DoryLayout::AddressMajor {
let log_T = DoryGlobals::get_T().log_2();
let log_K = opening_point.len().saturating_sub(log_T);
let (r_address, r_cycle) = opening_point.split_at(log_K);
[r_cycle, r_address].concat()
} else {
opening_point.to_vec()
}
}
168 changes: 137 additions & 31 deletions jolt-core/src/poly/commitment/dory/dory_globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::utils::math::Math;
use allocative::Allocative;
use dory::backends::arkworks::{init_cache, ArkG1, ArkG2};
use std::sync::{
atomic::{AtomicU8, Ordering},
atomic::{AtomicU8, AtomicUsize, Ordering},
RwLock,
};
#[cfg(test)]
Expand Down Expand Up @@ -143,6 +143,7 @@ impl From<DoryLayout> for u8 {

// Main polynomial globals
static GLOBAL_T: RwLock<Option<usize>> = RwLock::new(None);
static MAIN_K_CHUNK: RwLock<Option<usize>> = RwLock::new(None);
static MAX_NUM_ROWS: RwLock<Option<usize>> = RwLock::new(None);
static NUM_COLUMNS: RwLock<Option<usize>> = RwLock::new(None);

Expand All @@ -161,6 +162,8 @@ static CURRENT_CONTEXT: AtomicU8 = AtomicU8::new(0);

// Layout tracking: 0=CycleMajor, 1=AddressMajor
static CURRENT_LAYOUT: AtomicU8 = AtomicU8::new(0);
// Largest Main log-embedding needed for precommitted/embed calculations.
static MAIN_LOG_EMBEDDING: AtomicUsize = AtomicUsize::new(0);

/// Dory commitment context - determines which set of global parameters to use
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -256,6 +259,22 @@ impl DoryGlobals {
log_t.saturating_sub(sigma_main)
}

#[inline]
pub fn get_main_log_embedding() -> usize {
let stored = MAIN_LOG_EMBEDDING.load(Ordering::SeqCst);
if stored > 0 {
stored
} else {
let main_cols = Self::configured_main_num_columns();
let main_rows = *MAX_NUM_ROWS
.read()
.unwrap()
.as_ref()
.expect("main max_num_rows not initialized");
main_cols.log_2() + main_rows.log_2()
}
}

/// Get the current Dory context
pub fn current_context() -> DoryContext {
CURRENT_CONTEXT.load(Ordering::SeqCst).into()
Expand Down Expand Up @@ -288,11 +307,84 @@ impl DoryGlobals {
(Self::get_max_num_rows(), Self::get_num_columns())
}

#[inline]
pub(crate) fn main_k() -> usize {
*MAIN_K_CHUNK
.read()
.unwrap()
.as_ref()
.expect("main k not initialized")
}

#[inline]
pub(crate) fn main_t() -> usize {
*GLOBAL_T
.read()
.unwrap()
.as_ref()
.expect("main t not initialized")
}

#[inline]
pub(crate) fn configured_main_num_columns() -> usize {
*NUM_COLUMNS
.read()
.unwrap()
.as_ref()
.expect("main num_columns not initialized")
}

#[inline]
fn main_embedding_extra_vars() -> usize {
let main_total_vars = Self::main_k().log_2() + Self::get_T().log_2();
Self::get_main_log_embedding().saturating_sub(main_total_vars)
}

/// Column stride for one-hot embeddings in the current layout/context.
pub fn one_hot_stride() -> usize {
if Self::current_context() != DoryContext::Main
|| Self::get_layout() != DoryLayout::AddressMajor
{
return 1;
}
1usize << Self::main_embedding_extra_vars()
}

/// Column stride for dense trace-domain embeddings in the current layout/context.
pub fn dense_stride() -> usize {
if Self::current_context() != DoryContext::Main
|| Self::get_layout() != DoryLayout::AddressMajor
{
return 1;
}
let dense_stride_log = Self::main_embedding_extra_vars() + Self::main_k().log_2();
1usize << dense_stride_log
}

/// Returns the embedded cycle-domain size for the current Dory matrix.
pub fn get_embedded_t() -> usize {
let context = Self::current_context();
if context != DoryContext::Main {
return Self::get_T();
}

let k = Self::main_k();
let num_rows = Self::get_max_num_rows();
let num_cols = Self::get_num_columns();
let total = num_rows * num_cols;
debug_assert_eq!(
total % k,
0,
"Invalid Main DoryGlobals: num_rows*num_cols must be divisible by K"
);
total / k
}

/// Returns the "K" used to initialize the *main* Dory matrix for OneHot polynomials.
///
/// This is derived from the identity:
/// `K * T == num_rows * num_cols` (all values are powers of two in our usage).
pub fn k_from_matrix_shape() -> usize {
if Self::current_context() == DoryContext::Main {
return Self::main_k();
}
let (num_rows, num_cols) = Self::matrix_shape();
let t = Self::get_T();
debug_assert_eq!(
Expand All @@ -303,22 +395,6 @@ impl DoryGlobals {
(num_rows * num_cols) / t
}

/// For `AddressMajor`, each Dory matrix row corresponds to this many cycles.
///
/// Equivalent to `T / num_rows` and to `num_cols / K`.
pub fn address_major_cycles_per_row() -> usize {
let (num_rows, num_cols) = Self::matrix_shape();
let k = Self::k_from_matrix_shape();
debug_assert!(k > 0);
debug_assert_eq!(num_cols % k, 0, "Expected num_cols to be divisible by K");
debug_assert_eq!(
Self::get_T() % num_rows,
0,
"Expected T to be divisible by num_rows"
);
num_cols / k
}

fn set_max_num_rows_for_context(max_num_rows: usize, context: DoryContext) {
match context {
DoryContext::Main => {
Expand Down Expand Up @@ -365,6 +441,10 @@ impl DoryGlobals {
}
}

fn set_main_k(k: usize) {
*MAIN_K_CHUNK.write().unwrap() = Some(k);
}

pub fn get_num_columns() -> usize {
let context = Self::current_context();
match context {
Expand Down Expand Up @@ -430,6 +510,20 @@ impl DoryGlobals {
(num_columns, num_rows, T)
}

fn initialize_context_common(
K: usize,
embedded_t: usize,
stored_t: usize,
context: DoryContext,
) -> Option<()> {
let (num_columns, num_rows, _) = Self::calculate_dimensions(K, embedded_t);
Self::set_num_columns_for_context(num_columns, context);
Self::set_T_for_context(stored_t, context);
Self::set_max_num_rows_for_context(num_rows, context);

Some(())
}

/// Initialize the globals for a specific Dory context
///
/// # Arguments
Expand All @@ -451,20 +545,30 @@ impl DoryGlobals {
) -> Option<()> {
#[cfg(test)]
Self::configure_test_cache_root();

let (num_columns, num_rows, t) = Self::calculate_dimensions(K, T);
Self::set_num_columns_for_context(num_columns, context);
Self::set_T_for_context(t, context);
Self::set_max_num_rows_for_context(num_rows, context);

// For Main context, set layout (if provided) and ensure subsequent uses of `get_*` read from it
if context == DoryContext::Main {
if let Some(l) = layout {
CURRENT_LAYOUT.store(l as u8, Ordering::SeqCst);
}
CURRENT_CONTEXT.store(DoryContext::Main as u8, Ordering::SeqCst);
return Self::initialize_main_with_log_embedding(K, T, K.log_2() + T.log_2(), layout);
}
Self::initialize_context_common(K, T, T, context)?;
Some(())
}

/// Initialize Main context with execution `T` and explicit `main_log_embedding` for
/// global precommitted geometry.
pub fn initialize_main_with_log_embedding(
K: usize,
T: usize,
matrix_total_vars: usize,
layout: Option<DoryLayout>,
) -> Option<()> {
let log_k = K.log_2();
let embedded_t = 1usize << matrix_total_vars.saturating_sub(log_k);
Self::initialize_context_common(K, embedded_t, T, DoryContext::Main)?;
Self::set_main_k(K);
if let Some(l) = layout {
CURRENT_LAYOUT.store(l as u8, Ordering::SeqCst);
}
CURRENT_CONTEXT.store(DoryContext::Main as u8, Ordering::SeqCst);
MAIN_LOG_EMBEDDING.store(matrix_total_vars, Ordering::SeqCst);
Some(())
}

Expand All @@ -475,6 +579,7 @@ impl DoryGlobals {

// Reset main globals
*GLOBAL_T.write().unwrap() = None;
*MAIN_K_CHUNK.write().unwrap() = None;
*MAX_NUM_ROWS.write().unwrap() = None;
*NUM_COLUMNS.write().unwrap() = None;

Expand All @@ -492,6 +597,7 @@ impl DoryGlobals {
*UNTRUSTED_ADVICE_NUM_COLUMNS.write().unwrap() = None;

CURRENT_CONTEXT.store(0, Ordering::SeqCst);
MAIN_LOG_EMBEDDING.store(0, Ordering::SeqCst);
}

/// Initialize the prepared point cache for faster pairing operations
Expand Down
Loading