diff --git a/crates/jolt-akita/src/scheme.rs b/crates/jolt-akita/src/scheme.rs index f8e626ed1f..a4aab6d79e 100644 --- a/crates/jolt-akita/src/scheme.rs +++ b/crates/jolt-akita/src/scheme.rs @@ -417,6 +417,71 @@ mod tests { .expect_err("changed direct commitment digest should reject"); } + #[test] + fn sparse_native_polynomial_batch_opening_roundtrips_and_binds_commitment() { + let num_vars = 6; + let setup_params = AkitaSetupParams::new(num_vars, 1, [19; 32]); + let (prover_setup, verifier_setup) = AkitaScheme::setup(setup_params); + let unit_indices = [0, 3, 17]; + let sparse = AkitaSparsePolynomial::from_jolt_unit_indices(num_vars, unit_indices) + .expect("sparse polynomial should build"); + let (commitment, hint) = + AkitaScheme::commit_sparse_polynomial(&prover_setup, [23; 32], &sparse) + .expect("sparse commitment should build"); + let mut dense = vec![AkitaField::zero(); 1 << num_vars]; + for index in unit_indices { + dense[index] = AkitaField::one(); + } + let point = (0..num_vars) + .map(|index| AkitaField::from_u64(index as u64 + 2)) + .collect::>(); + let claim = Polynomial::new(dense).evaluate(&point); + let statement = BatchOpeningStatement { + logical_point: point.clone(), + pcs_point: point, + layout_digest: commitment.layout_digest, + claims: vec![BatchOpeningClaim { + id: (), + relation: (), + commitment: commitment.clone(), + claim, + view: PhysicalView::Direct, + scale: AkitaField::one(), + }], + }; + + let mut prover_transcript = RecordingTranscript::new(b"akita-sparse-native"); + let proof = AkitaScheme::prove_sparse_batch( + &prover_setup, + &mut prover_transcript, + &statement, + &sparse, + hint, + ) + .expect("sparse proof should prove"); + assert_eq!(proof.commitment, commitment); + + let mut verifier_transcript = RecordingTranscript::new(b"akita-sparse-native"); + let _result = AkitaScheme::verify_batch( + &verifier_setup, + &mut verifier_transcript, + &statement, + &proof, + ) + .expect("sparse proof should verify"); + + let mut tampered_proof = proof; + tampered_proof.commitment.layout_digest = [42; 32]; + let mut verifier_transcript = RecordingTranscript::new(b"akita-sparse-native"); + let _error = AkitaScheme::verify_batch( + &verifier_setup, + &mut verifier_transcript, + &statement, + &tampered_proof, + ) + .expect_err("tampered proof commitment should reject"); + } + fn contains_subslice(haystack: &[u8], needle: &[u8]) -> bool { haystack .windows(needle.len()) diff --git a/crates/jolt-claims/src/lib.rs b/crates/jolt-claims/src/lib.rs index 9656b9b84f..6945d9afdb 100644 --- a/crates/jolt-claims/src/lib.rs +++ b/crates/jolt-claims/src/lib.rs @@ -1,4 +1,11 @@ //! Shared claim and expression types for Jolt protocols. +//! +//! This crate owns protocol semantics: symbolic claim expressions, typed opening +//! and challenge identifiers, relation metadata, and Jolt-specific formula +//! builders. The lattice/Akita surface follows the same rule: `jolt-claims` +//! names Jolt facts, logical view formulas, and validity requirements, while +//! physical packing layouts and PCS transport remain in `jolt-openings`, +//! `jolt-verifier`, and the backend adapter crates. mod claims; mod ops; diff --git a/crates/jolt-claims/src/protocols/field_inline/formulas/lattice.rs b/crates/jolt-claims/src/protocols/field_inline/formulas/lattice.rs index 01f317e9dd..d9512e3f1b 100644 --- a/crates/jolt-claims/src/protocols/field_inline/formulas/lattice.rs +++ b/crates/jolt-claims/src/protocols/field_inline/formulas/lattice.rs @@ -1,9 +1,7 @@ use jolt_field::Field; -use jolt_openings::{ - PackingFamilyId, PackingValidityRequirement, PackingViewFormula, PackingViewTerm, -}; +use jolt_openings::{PackingValidityRequirement, PackingViewFormula, PackingViewTerm}; -use crate::protocols::jolt::weighted_byte_decode_terms; +use crate::protocols::jolt::{weighted_byte_decode_terms, JoltPackingFamilyId}; pub fn field_rd_inc_lattice_view_formula(byte_width: usize) -> PackingViewFormula { PackingViewFormula::linear_decoded(field_rd_inc_byte_terms(byte_width)) @@ -14,7 +12,7 @@ pub fn field_rd_inc_byte_terms(byte_width: usize) -> Vec Vec PackingValidityRequirement { PackingValidityRequirement::field_element_canonical_bytes( - PackingFamilyId::FieldRdIncByte { index: 0 }, + JoltPackingFamilyId::FieldRdIncByte { index: 0 }.into(), byte_width, modulus, ) @@ -58,7 +56,7 @@ mod tests { assert_eq!(terms[7].coefficient, Fr::from_u64(7)); assert_eq!( terms[7].family, - PackingFamilyId::FieldRdIncByte { index: 0 } + JoltPackingFamilyId::FieldRdIncByte { index: 0 }.into() ); assert_eq!(terms[7].limb, 0); assert_eq!(terms[7].symbol, 7); @@ -67,7 +65,7 @@ mod tests { assert_eq!(second_byte.coefficient, Fr::from_u64(3 * 256)); assert_eq!( second_byte.family, - PackingFamilyId::FieldRdIncByte { index: 1 } + JoltPackingFamilyId::FieldRdIncByte { index: 1 }.into() ); assert_eq!(second_byte.limb, 0); assert_eq!(second_byte.symbol, 3); @@ -79,12 +77,12 @@ mod tests { field_rd_inc_validity_requirements(2), vec![ PackingValidityRequirement::exact_one_hot( - PackingFamilyId::FieldRdIncByte { index: 0 }, + JoltPackingFamilyId::FieldRdIncByte { index: 0 }.into(), 1, 256, ), PackingValidityRequirement::exact_one_hot( - PackingFamilyId::FieldRdIncByte { index: 1 }, + JoltPackingFamilyId::FieldRdIncByte { index: 1 }.into(), 1, 256, ), @@ -97,7 +95,7 @@ mod tests { assert_eq!( field_rd_inc_canonical_bytes_requirement(2, 257), PackingValidityRequirement::field_element_canonical_bytes( - PackingFamilyId::FieldRdIncByte { index: 0 }, + JoltPackingFamilyId::FieldRdIncByte { index: 0 }.into(), 2, 257, ) diff --git a/crates/jolt-claims/src/protocols/jolt/formulas/lattice.rs b/crates/jolt-claims/src/protocols/jolt/formulas/lattice.rs index 254864f449..e998f3d7de 100644 --- a/crates/jolt-claims/src/protocols/jolt/formulas/lattice.rs +++ b/crates/jolt-claims/src/protocols/jolt/formulas/lattice.rs @@ -1,1314 +1,49 @@ -use jolt_field::{Field, FromPrimitiveInt, RingCore}; -use jolt_lookup_tables::{LookupTableKind, XLEN}; -use jolt_poly::EqPolynomial; -use jolt_riscv::{NUM_CIRCUIT_FLAGS, NUM_INSTRUCTION_FLAGS}; -use serde::{Deserialize, Serialize}; - -use crate::{challenge, constant, opening, public}; - -use super::super::{ - IncVirtualizationChallenge, IncVirtualizationPublic, JoltChallengeId, JoltExpr, JoltPublicId, - JoltRelationClaims, UnsignedIncChunkReconstructionChallenge, - UnsignedIncChunkReconstructionPublic, -}; -use super::super::{JoltAdviceKind, JoltCommittedPolynomial, JoltOpeningId, JoltRelationId}; -use super::claim_reductions::bytecode as bytecode_reduction; -use super::dimensions::{ - JoltFormulaPointError, JoltSumcheckSpec, TraceDimensions, TracePolynomialOrder, - REGISTER_ADDRESS_BITS, -}; -use jolt_riscv::CircuitFlags; +mod families; +mod layout; +mod openings; +mod relations; +#[cfg(test)] +mod tests; +mod validity; +mod views; pub const UNSIGNED_INC_BITS: usize = 64; pub use jolt_openings::{ - packing_validity_digest, PackingAdviceKind, PackingFamilyId, PackingValidityDigest, - PackingValidityKind, PackingValidityRequirement, PackingViewFormula, PackingViewTerm, + packing_validity_digest, PackingAdviceKind, PackingValidityDigest, PackingValidityKind, + PackingValidityRequirement, PackingViewFormula, PackingViewTerm, }; -pub fn inc_virtualization_relation() -> JoltRelationId { - JoltRelationId::IncVirtualization -} - -pub fn unsigned_inc_claim_reduction_relation() -> JoltRelationId { - JoltRelationId::UnsignedIncClaimReduction -} - -pub fn unsigned_inc_chunk_reconstruction_relation() -> JoltRelationId { - JoltRelationId::UnsignedIncChunkReconstruction -} - -pub fn inc_virtualization_claim(dimensions: TraceDimensions) -> JoltRelationClaims -where - F: RingCore, -{ - let gamma = inc_virtualization_challenge(IncVirtualizationChallenge::Gamma); - - let input = opening(inc_virtualization_ram_read_write_opening()) - + gamma.clone() * opening(inc_virtualization_ram_val_check_opening()) - + gamma.clone().pow(2) * opening(inc_virtualization_rd_read_write_opening()) - + gamma.clone().pow(3) * opening(inc_virtualization_rd_val_evaluation_opening()); - - let ram_coeff = inc_virtualization_public(IncVirtualizationPublic::EqRamReadWrite) - + gamma.clone() * inc_virtualization_public(IncVirtualizationPublic::EqRamValCheck); - let gamma_2 = gamma.clone().pow(2); - let rd_coeff = inc_virtualization_public(IncVirtualizationPublic::EqRegistersReadWrite) - + gamma.clone() - * inc_virtualization_public(IncVirtualizationPublic::EqRegistersValEvaluation); - let store = opening(inc_virtualization_store_opening()); - let output = opening(inc_virtualization_inc_opening()) - * (ram_coeff * store.clone() + gamma_2 * rd_coeff * (JoltExpr::one() - store)); - - JoltRelationClaims::new( - JoltRelationId::IncVirtualization, - dimensions.sumcheck(3), - input, - output, - ) -} - -pub fn unsigned_inc_claim_reduction_claim(dimensions: TraceDimensions) -> JoltRelationClaims -where - F: RingCore + FromPrimitiveInt, -{ - let input = opening(inc_virtualization_inc_opening()) + constant(F::from_u128(1u128 << 64)); - let output = opening(unsigned_inc_opening()); - - JoltRelationClaims::new( - JoltRelationId::UnsignedIncClaimReduction, - dimensions.sumcheck(2), - input, - output, - ) -} - -pub fn unsigned_inc_msb_booleanity_claim(dimensions: TraceDimensions) -> JoltRelationClaims -where - F: RingCore, -{ - let msb = opening(unsigned_inc_msb_opening()); - JoltRelationClaims::new( - JoltRelationId::Booleanity, - dimensions.sumcheck(2), - JoltExpr::zero(), - msb.clone() * msb.clone() - msb, - ) -} - -fn inc_virtualization_challenge(id: IncVirtualizationChallenge) -> JoltExpr -where - F: RingCore, -{ - challenge(JoltChallengeId::from(id)) -} - -fn inc_virtualization_public(id: IncVirtualizationPublic) -> JoltExpr -where - F: RingCore, -{ - public(JoltPublicId::from(id)) -} - -fn unsigned_inc_chunk_reconstruction_challenge( - id: UnsignedIncChunkReconstructionChallenge, -) -> JoltExpr -where - F: RingCore, -{ - challenge(JoltChallengeId::from(id)) -} - -fn unsigned_inc_chunk_reconstruction_public( - id: UnsignedIncChunkReconstructionPublic, -) -> JoltExpr -where - F: RingCore, -{ - public(JoltPublicId::from(id)) -} - -pub fn inc_virtualization_input_openings() -> [JoltOpeningId; 4] { - [ - inc_virtualization_ram_read_write_opening(), - inc_virtualization_ram_val_check_opening(), - inc_virtualization_rd_read_write_opening(), - inc_virtualization_rd_val_evaluation_opening(), - ] -} - -pub fn inc_virtualization_output_openings() -> [JoltOpeningId; 2] { - [ - inc_virtualization_inc_opening(), - inc_virtualization_store_opening(), - ] -} - -pub fn inc_virtualization_ram_read_write_opening() -> JoltOpeningId { - JoltOpeningId::committed( - JoltCommittedPolynomial::RamInc, - JoltRelationId::RamReadWriteChecking, - ) -} - -pub fn inc_virtualization_ram_val_check_opening() -> JoltOpeningId { - JoltOpeningId::committed(JoltCommittedPolynomial::RamInc, JoltRelationId::RamValCheck) -} - -pub fn inc_virtualization_rd_read_write_opening() -> JoltOpeningId { - JoltOpeningId::committed( - JoltCommittedPolynomial::RdInc, - JoltRelationId::RegistersReadWriteChecking, - ) -} - -pub fn inc_virtualization_rd_val_evaluation_opening() -> JoltOpeningId { - JoltOpeningId::committed( - JoltCommittedPolynomial::RdInc, - JoltRelationId::RegistersValEvaluation, - ) -} - -pub fn inc_virtualization_inc_opening() -> JoltOpeningId { - JoltOpeningId::lattice(JoltRelationId::IncVirtualization, 0) -} - -pub fn inc_virtualization_store_opening() -> JoltOpeningId { - JoltOpeningId::lattice(JoltRelationId::IncVirtualization, 1) -} - -pub fn unsigned_inc_input_opening() -> JoltOpeningId { - inc_virtualization_inc_opening() -} - -pub fn unsigned_inc_opening() -> JoltOpeningId { - JoltOpeningId::lattice(JoltRelationId::UnsignedIncClaimReduction, 0) -} - -pub fn unsigned_inc_msb_opening() -> JoltOpeningId { - JoltOpeningId::lattice(JoltRelationId::UnsignedIncClaimReduction, 1) -} - -pub fn unsigned_inc_chunk_opening(index: usize) -> JoltOpeningId { - JoltOpeningId::lattice(JoltRelationId::UnsignedIncClaimReduction, 2 + index) -} - -pub fn unsigned_inc_lower_chunk_count(log_k_chunk: usize) -> Option { - (log_k_chunk != 0 && UNSIGNED_INC_BITS.is_multiple_of(log_k_chunk)) - .then_some(UNSIGNED_INC_BITS / log_k_chunk) -} - -pub fn unsigned_inc_chunk_reconstruction_claim( - log_k_chunk: usize, -) -> Option> -where - F: RingCore + FromPrimitiveInt, -{ - let chunk_count = unsigned_inc_lower_chunk_count(log_k_chunk)?; - let gamma = - unsigned_inc_chunk_reconstruction_challenge(UnsignedIncChunkReconstructionChallenge::Gamma); - let eq_booleanity_address = unsigned_inc_chunk_reconstruction_public( - UnsignedIncChunkReconstructionPublic::EqBooleanityAddress, - ); - let identity_at_address = unsigned_inc_chunk_reconstruction_public( - UnsignedIncChunkReconstructionPublic::IdentityAtAddress, - ); - let delta = gamma.clone().pow(2 * chunk_count); - let lower_value = opening(unsigned_inc_opening()) - - constant(F::from_u128(1u128 << 64)) * opening(unsigned_inc_msb_opening()); - - let mut input = delta.clone() * lower_value; - let mut output = JoltExpr::zero(); - let mut place = F::one(); - for index in 0..chunk_count { - input = input - + gamma.clone().pow(2 * index) - + gamma.clone().pow(2 * index + 1) * opening(unsigned_inc_chunk_opening(index)); - let output_coeff = gamma.clone().pow(2 * index) - + gamma.clone().pow(2 * index + 1) * eq_booleanity_address.clone() - + delta.clone() * constant(place) * identity_at_address.clone(); - output = output + output_coeff * opening(unsigned_inc_chunk_opening(index)); - place *= F::from_u64(1u64 << log_k_chunk); - } - - Some( - JoltRelationClaims::new( - JoltRelationId::UnsignedIncChunkReconstruction, - JoltSumcheckSpec::boolean(log_k_chunk, 3), - input, - output, - ) - .with_input_challenges([JoltChallengeId::from( - UnsignedIncChunkReconstructionChallenge::Gamma, - )]), - ) -} - -pub fn bytecode_store_flag_lattice_view_formula(chunk: usize) -> PackingViewFormula { - PackingViewFormula::direct( - PackingFamilyId::BytecodeCircuitFlag { - chunk, - flag: CircuitFlags::Store as usize, - }, - 0, - 1, - ) -} - -pub fn bytecode_rd_present_lattice_view_formula(chunk: usize) -> PackingViewFormula { - PackingViewFormula::linear_decoded(weighted_symbol_terms( - PackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }, - 0, - [F::one(); 1 << REGISTER_ADDRESS_BITS], - )) -} - -pub fn unsigned_inc_lower_value_lattice_view_formula( - log_k_chunk: usize, -) -> Option> { - Some(PackingViewFormula::linear_decoded( - unsigned_inc_lower_value_terms(log_k_chunk)?, - )) -} - -pub fn unsigned_inc_lower_value_terms( - log_k_chunk: usize, -) -> Option>> { - let chunk_count = unsigned_inc_lower_chunk_count(log_k_chunk)?; - let alphabet_size = 1usize << log_k_chunk; - let mut terms = Vec::with_capacity(chunk_count * alphabet_size); - let mut place = F::one(); - for index in 0..chunk_count { - terms.extend(weighted_symbol_terms( - PackingFamilyId::UnsignedIncChunk { index }, - 0, - (0..alphabet_size).map(|symbol| place * F::from_u64(symbol as u64)), - )); - place *= F::from_u64(1u64 << log_k_chunk); - } - Some(terms) -} - -pub fn unsigned_inc_msb_lattice_view_formula() -> PackingViewFormula { - PackingViewFormula::direct(PackingFamilyId::UnsignedIncMsb, 0, 1) -} - -pub fn unsigned_inc_validity_requirements( - log_k_chunk: usize, -) -> Option> { - let chunk_count = unsigned_inc_lower_chunk_count(log_k_chunk)?; - let alphabet_size = 1usize << log_k_chunk; - let mut requirements = (0..chunk_count) - .map(|index| { - PackingValidityRequirement::exact_one_hot( - PackingFamilyId::UnsignedIncChunk { index }, - 1, - alphabet_size, - ) - }) - .collect::>(); - requirements.push(PackingValidityRequirement::boolean_indicator( - PackingFamilyId::UnsignedIncMsb, - 1, - 2, - 1, - )); - Some(requirements) -} - -pub fn advice_bytes_validity_requirement(kind: JoltAdviceKind) -> PackingValidityRequirement { - byte_validity_requirement( - PackingFamilyId::AdviceBytes { - kind: packing_advice_kind(kind), - index: 0, - }, - 1, - ) -} - -pub fn program_image_validity_requirement() -> PackingValidityRequirement { - byte_validity_requirement(PackingFamilyId::ProgramImageInit, 8) -} - -pub fn bytecode_validity_requirements( - chunk: usize, - field_byte_width: usize, -) -> Vec { - let register_count = 1usize << REGISTER_ADDRESS_BITS; - let mut requirements = Vec::new(); - for selector in 0..3 { - requirements.push(PackingValidityRequirement::optional_one_hot( - PackingFamilyId::BytecodeRegisterSelector { chunk, selector }, - 1, - register_count, - )); - } - for flag in 0..NUM_CIRCUIT_FLAGS { - requirements.push(PackingValidityRequirement::boolean_indicator( - PackingFamilyId::BytecodeCircuitFlag { chunk, flag }, - 1, - 2, - 1, - )); - } - for flag in 0..NUM_INSTRUCTION_FLAGS { - requirements.push(PackingValidityRequirement::boolean_indicator( - PackingFamilyId::BytecodeInstructionFlag { chunk, flag }, - 1, - 2, - 1, - )); - } - requirements.push(PackingValidityRequirement::optional_one_hot( - PackingFamilyId::BytecodeLookupSelector { chunk }, - 1, - LookupTableKind::::COUNT.next_power_of_two(), - )); - requirements.push(PackingValidityRequirement::boolean_indicator( - PackingFamilyId::BytecodeRafFlag { chunk }, - 1, - 2, - 1, - )); - requirements.push(byte_validity_requirement( - PackingFamilyId::BytecodeUnexpandedPcBytes { chunk }, - 8, - )); - requirements.push(byte_validity_requirement( - PackingFamilyId::BytecodeImmBytes { chunk }, - field_byte_width, - )); - requirements.push(PackingValidityRequirement::bytecode_store_rd_disjoint( - chunk, - CircuitFlags::Store as usize, - )); - requirements -} - -pub fn bytecode_imm_canonical_bytes_requirement( - chunk: usize, - byte_width: usize, - modulus: u128, -) -> PackingValidityRequirement { - PackingValidityRequirement::field_element_canonical_bytes( - PackingFamilyId::BytecodeImmBytes { chunk }, - byte_width, - modulus, - ) -} - -fn byte_validity_requirement(family: PackingFamilyId, limbs: usize) -> PackingValidityRequirement { - PackingValidityRequirement::exact_one_hot(family, limbs, 256) -} - -fn packing_advice_kind(kind: JoltAdviceKind) -> PackingAdviceKind { - match kind { - JoltAdviceKind::Trusted => PackingAdviceKind::Trusted, - JoltAdviceKind::Untrusted => PackingAdviceKind::Untrusted, - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum LatticeFinalOpeningRequirement { - PackingLayoutFamily { - family: PackingFamilyId, - relation: JoltRelationId, - }, - LogicalOnly, -} - -pub fn final_opening_lattice_requirement( - polynomial: JoltCommittedPolynomial, -) -> LatticeFinalOpeningRequirement { - match polynomial { - JoltCommittedPolynomial::RdInc | JoltCommittedPolynomial::RamInc => { - LatticeFinalOpeningRequirement::LogicalOnly - } - JoltCommittedPolynomial::InstructionRa(index) => packed_family_requirement( - PackingFamilyId::InstructionRa { index }, - JoltRelationId::HammingWeightClaimReduction, - ), - JoltCommittedPolynomial::BytecodeRa(index) => packed_family_requirement( - PackingFamilyId::BytecodeRa { index }, - JoltRelationId::HammingWeightClaimReduction, - ), - JoltCommittedPolynomial::RamRa(index) => packed_family_requirement( - PackingFamilyId::RamRa { index }, - JoltRelationId::HammingWeightClaimReduction, - ), - JoltCommittedPolynomial::TrustedAdvice => packed_family_requirement( - PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Trusted, - index: 0, - }, - JoltRelationId::AdviceClaimReduction, - ), - JoltCommittedPolynomial::UntrustedAdvice => packed_family_requirement( - PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Untrusted, - index: 0, - }, - JoltRelationId::AdviceClaimReduction, - ), - JoltCommittedPolynomial::BytecodeChunk(index) => packed_family_requirement( - PackingFamilyId::BytecodeChunk { index }, - JoltRelationId::BytecodeClaimReduction, - ), - JoltCommittedPolynomial::ProgramImageInit => packed_family_requirement( - PackingFamilyId::ProgramImageInit, - JoltRelationId::ProgramImageClaimReduction, - ), - } -} - -fn packed_family_requirement( - family: PackingFamilyId, - relation: JoltRelationId, -) -> LatticeFinalOpeningRequirement { - LatticeFinalOpeningRequirement::PackingLayoutFamily { family, relation } -} - -pub fn byte_decode_terms( - family: PackingFamilyId, - limb: usize, -) -> Vec> { - weighted_byte_decode_terms(family, [(limb, F::one())]) -} - -pub fn symbol_decode_terms( - family: PackingFamilyId, - limb: usize, - alphabet_size: usize, -) -> Vec> { - weighted_symbol_terms( - family, - limb, - (0..alphabet_size).map(|symbol| F::from_u64(symbol as u64)), - ) -} - -pub fn weighted_symbol_terms( - family: PackingFamilyId, - limb: usize, - weights: impl IntoIterator, -) -> Vec> { - weights - .into_iter() - .enumerate() - .map(|(symbol, coefficient)| { - PackingViewTerm::new(coefficient, family.clone(), limb, symbol) - }) - .collect() -} - -pub fn weighted_byte_decode_terms( - family: PackingFamilyId, - limb_weights: impl IntoIterator, -) -> Vec> { - limb_weights - .into_iter() - .flat_map(|(limb, limb_weight)| { - let family = family.clone(); - (0..256).map(move |symbol| { - PackingViewTerm::new( - limb_weight * F::from_u64(symbol as u64), - family.clone(), - limb, - symbol, - ) - }) - }) - .collect() -} - -pub fn little_endian_byte_decode_terms( - family: PackingFamilyId, - limb_count: usize, -) -> Vec> { - let mut limb_weights = Vec::with_capacity(limb_count); - let mut place = F::one(); - for limb in 0..limb_count { - limb_weights.push((limb, place)); - place *= F::from_u64(256); - } - weighted_byte_decode_terms(family, limb_weights) -} - -pub fn bytecode_chunk_lattice_view_formula( - chunk: usize, - opening_point: &[F], - trace_order: TracePolynomialOrder, - log_bytecode: usize, - field_byte_width: usize, -) -> Result, JoltFormulaPointError> { - let lane_vars = bytecode_reduction::committed_lane_vars(); - let expected = lane_vars + log_bytecode; - if opening_point.len() != expected { - return Err(JoltFormulaPointError::OpeningPointLengthMismatch { - expected, - got: opening_point.len(), - }); - } - let lane_point = match trace_order { - TracePolynomialOrder::CycleMajor => &opening_point[..lane_vars], - TracePolynomialOrder::AddressMajor => &opening_point[log_bytecode..], - }; - let lane_weights = EqPolynomial::::evals(lane_point, None); - let lane_layout = bytecode_reduction::BYTECODE_LANE_LAYOUT; - let register_count = 1usize << REGISTER_ADDRESS_BITS; - let mut terms = Vec::new(); - - for selector in 0..3 { - let start = match selector { - 0 => lane_layout.rs1_start, - 1 => lane_layout.rs2_start, - _ => lane_layout.rd_start, - }; - terms.extend(weighted_symbol_terms( - PackingFamilyId::BytecodeRegisterSelector { chunk, selector }, - 0, - lane_weights[start..start + register_count].iter().copied(), - )); - } - terms.extend(weighted_byte_decode_terms( - PackingFamilyId::BytecodeUnexpandedPcBytes { chunk }, - byte_limb_weights(lane_weights[lane_layout.unexp_pc_idx], 8), - )); - terms.extend(weighted_byte_decode_terms( - PackingFamilyId::BytecodeImmBytes { chunk }, - byte_limb_weights(lane_weights[lane_layout.imm_idx], field_byte_width), - )); - for flag in 0..NUM_CIRCUIT_FLAGS { - terms.push(PackingViewTerm::new( - lane_weights[lane_layout.circuit_start + flag], - PackingFamilyId::BytecodeCircuitFlag { chunk, flag }, - 0, - 1, - )); - } - for flag in 0..NUM_INSTRUCTION_FLAGS { - terms.push(PackingViewTerm::new( - lane_weights[lane_layout.instr_start + flag], - PackingFamilyId::BytecodeInstructionFlag { chunk, flag }, - 0, - 1, - )); - } - terms.extend(weighted_symbol_terms( - PackingFamilyId::BytecodeLookupSelector { chunk }, - 0, - lane_weights - [lane_layout.lookup_start..lane_layout.lookup_start + LookupTableKind::::COUNT] - .iter() - .copied(), - )); - terms.push(PackingViewTerm::new( - lane_weights[lane_layout.raf_flag_idx], - PackingFamilyId::BytecodeRafFlag { chunk }, - 0, - 1, - )); - - Ok(PackingViewFormula::linear_decoded(terms)) -} - -fn byte_limb_weights(lane_weight: F, limb_count: usize) -> Vec<(usize, F)> { - let mut weights = Vec::with_capacity(limb_count); - let mut place = F::one(); - for limb in 0..limb_count { - weights.push((limb, lane_weight * place)); - place *= F::from_u64(256); - } - weights -} - -#[cfg(test)] -mod tests { - #![expect( - clippy::expect_used, - clippy::panic, - clippy::unwrap_used, - reason = "tests fail loudly on unexpected errors" - )] - - use super::*; - use jolt_field::{Fr, FromPrimitiveInt}; - - #[test] - fn final_opening_lattice_requirement_marks_increments_as_logical_only() { - assert_eq!( - final_opening_lattice_requirement(JoltCommittedPolynomial::RamInc), - LatticeFinalOpeningRequirement::LogicalOnly - ); - assert_eq!( - final_opening_lattice_requirement(JoltCommittedPolynomial::RdInc), - LatticeFinalOpeningRequirement::LogicalOnly - ); - } - - #[test] - fn final_opening_lattice_requirement_names_packed_families() { - assert_eq!( - final_opening_lattice_requirement(JoltCommittedPolynomial::InstructionRa(2)), - LatticeFinalOpeningRequirement::PackingLayoutFamily { - family: PackingFamilyId::InstructionRa { index: 2 }, - relation: JoltRelationId::HammingWeightClaimReduction, - } - ); - assert_eq!( - final_opening_lattice_requirement(JoltCommittedPolynomial::ProgramImageInit), - LatticeFinalOpeningRequirement::PackingLayoutFamily { - family: PackingFamilyId::ProgramImageInit, - relation: JoltRelationId::ProgramImageClaimReduction, - } - ); - } - - #[test] - fn inc_virtualization_claim_exposes_expected_dependencies() { - let claims = inc_virtualization_claim::(TraceDimensions::new(5)); - - assert_eq!(claims.id, JoltRelationId::IncVirtualization); - assert_eq!(claims.sumcheck, TraceDimensions::new(5).sumcheck(3)); - assert_eq!( - claims.input.required_openings, - inc_virtualization_input_openings() - ); - assert_eq!( - claims.output.required_openings, - inc_virtualization_output_openings() - ); - assert_eq!( - claims.required_challenges(), - vec![JoltChallengeId::from(IncVirtualizationChallenge::Gamma)] - ); - assert_eq!( - claims.required_publics(), - vec![ - JoltPublicId::from(IncVirtualizationPublic::EqRamReadWrite), - JoltPublicId::from(IncVirtualizationPublic::EqRamValCheck), - JoltPublicId::from(IncVirtualizationPublic::EqRegistersReadWrite), - JoltPublicId::from(IncVirtualizationPublic::EqRegistersValEvaluation), - ] - ); - } - - #[test] - fn inc_virtualization_claim_evaluates_store_selected_inc() { - let claims = inc_virtualization_claim::(TraceDimensions::new(5)); - let ram_rw = Fr::from_u64(3); - let ram_val = Fr::from_u64(5); - let rd_rw = Fr::from_u64(7); - let rd_val = Fr::from_u64(11); - let inc = Fr::from_u64(13); - let store = Fr::from_u64(0); - let eq_ram_rw = Fr::from_u64(17); - let eq_ram_val = Fr::from_u64(19); - let eq_rd_rw = Fr::from_u64(23); - let eq_rd_val = Fr::from_u64(29); - let gamma = Fr::from_u64(31); - let zero = Fr::from_u64(0); - - let input = claims.input.expression().evaluate( - |id| match *id { - id if id == inc_virtualization_ram_read_write_opening() => ram_rw, - id if id == inc_virtualization_ram_val_check_opening() => ram_val, - id if id == inc_virtualization_rd_read_write_opening() => rd_rw, - id if id == inc_virtualization_rd_val_evaluation_opening() => rd_val, - _ => zero, - }, - |id| match id { - JoltChallengeId::IncVirtualization(IncVirtualizationChallenge::Gamma) => gamma, - _ => zero, - }, - |_| zero, - ); - let output = claims.output.expression().evaluate( - |id| match *id { - id if id == inc_virtualization_inc_opening() => inc, - id if id == inc_virtualization_store_opening() => store, - _ => zero, - }, - |id| match id { - JoltChallengeId::IncVirtualization(IncVirtualizationChallenge::Gamma) => gamma, - _ => zero, - }, - |id| match *id { - JoltPublicId::IncVirtualization(IncVirtualizationPublic::EqRamReadWrite) => { - eq_ram_rw - } - JoltPublicId::IncVirtualization(IncVirtualizationPublic::EqRamValCheck) => { - eq_ram_val - } - JoltPublicId::IncVirtualization(IncVirtualizationPublic::EqRegistersReadWrite) => { - eq_rd_rw - } - JoltPublicId::IncVirtualization( - IncVirtualizationPublic::EqRegistersValEvaluation, - ) => eq_rd_val, - _ => zero, - }, - ); - - let gamma_2 = gamma * gamma; - assert_eq!( - input, - ram_rw + gamma * ram_val + gamma_2 * rd_rw + gamma_2 * gamma * rd_val - ); - assert_eq!(output, inc * gamma_2 * (eq_rd_rw + gamma * eq_rd_val)); - - let store_output = claims.output.expression().evaluate( - |id| match *id { - id if id == inc_virtualization_inc_opening() => inc, - id if id == inc_virtualization_store_opening() => Fr::from_u64(1), - _ => zero, - }, - |id| match id { - JoltChallengeId::IncVirtualization(IncVirtualizationChallenge::Gamma) => gamma, - _ => zero, - }, - |id| match *id { - JoltPublicId::IncVirtualization(IncVirtualizationPublic::EqRamReadWrite) => { - eq_ram_rw - } - JoltPublicId::IncVirtualization(IncVirtualizationPublic::EqRamValCheck) => { - eq_ram_val - } - JoltPublicId::IncVirtualization(IncVirtualizationPublic::EqRegistersReadWrite) => { - eq_rd_rw - } - JoltPublicId::IncVirtualization( - IncVirtualizationPublic::EqRegistersValEvaluation, - ) => eq_rd_val, - _ => zero, - }, - ); - assert_eq!(store_output, inc * (eq_ram_rw + gamma * eq_ram_val)); - } - - #[test] - fn unsigned_inc_claim_reduction_offsets_inc_by_two_to_64() { - let claims = unsigned_inc_claim_reduction_claim::(TraceDimensions::new(5)); - let inc = Fr::from_u64(13); - let unsigned_inc = Fr::from_u128((1u128 << 64) + 13); - let zero = Fr::from_u64(0); - - assert_eq!(claims.id, JoltRelationId::UnsignedIncClaimReduction); - assert_eq!(claims.sumcheck, TraceDimensions::new(5).sumcheck(2)); - assert_eq!( - claims.input.required_openings, - vec![inc_virtualization_inc_opening()] - ); - assert_eq!( - claims.output.required_openings, - vec![unsigned_inc_opening()] - ); - assert!(claims.required_challenges().is_empty()); - assert!(claims.required_publics().is_empty()); - - let input = claims.input.expression().evaluate( - |id| match *id { - id if id == inc_virtualization_inc_opening() => inc, - _ => zero, - }, - |_| zero, - |_| zero, - ); - let output = claims.output.expression().evaluate( - |id| match *id { - id if id == unsigned_inc_opening() => unsigned_inc, - _ => zero, - }, - |_| zero, - |_| zero, - ); - - assert_eq!(input, unsigned_inc); - assert_eq!(output, unsigned_inc); - } - - #[test] - fn unsigned_inc_msb_booleanity_claim_checks_cycle_bit() { - let claims = unsigned_inc_msb_booleanity_claim::(TraceDimensions::new(5)); - let zero = Fr::from_u64(0); - let msb = Fr::from_u64(7); - - assert_eq!(claims.id, JoltRelationId::Booleanity); - assert_eq!(claims.sumcheck, TraceDimensions::new(5).sumcheck(2)); - assert!(claims.input.required_openings.is_empty()); - assert_eq!( - claims.output.required_openings, - vec![unsigned_inc_msb_opening()] - ); - assert!(claims.required_challenges().is_empty()); - assert!(claims.required_publics().is_empty()); - - let output = claims.output.expression().evaluate( - |id| match *id { - id if id == unsigned_inc_msb_opening() => msb, - _ => zero, - }, - |_| zero, - |_| zero, - ); - - assert_eq!(output, msb * msb - msb); - } - - #[test] - fn unsigned_inc_chunk_reconstruction_claim_batches_hamming_point_and_value() { - let claims = unsigned_inc_chunk_reconstruction_claim::(8) - .expect("8-bit chunking should be valid"); - - assert_eq!(claims.id, JoltRelationId::UnsignedIncChunkReconstruction); - assert_eq!(claims.sumcheck, JoltSumcheckSpec::boolean(8, 3)); - assert!(claims - .input - .required_openings - .contains(&unsigned_inc_opening())); - assert!(claims - .input - .required_openings - .contains(&unsigned_inc_msb_opening())); - assert_eq!( - claims.output.required_openings, - (0..8).map(unsigned_inc_chunk_opening).collect::>() - ); - assert_eq!( - claims.required_challenges(), - vec![JoltChallengeId::from( - UnsignedIncChunkReconstructionChallenge::Gamma, - )] - ); - assert_eq!( - claims.required_publics(), - vec![ - JoltPublicId::from(UnsignedIncChunkReconstructionPublic::EqBooleanityAddress), - JoltPublicId::from(UnsignedIncChunkReconstructionPublic::IdentityAtAddress), - ] - ); - } - - #[test] - fn unsigned_inc_decode_formulas_use_configured_chunks_and_msb() { - assert_eq!( - unsigned_inc_msb_lattice_view_formula::(), - PackingViewFormula::direct(PackingFamilyId::UnsignedIncMsb, 0, 1) - ); - - let lower = unsigned_inc_lower_value_lattice_view_formula::(4) - .expect("4-bit chunking should be valid"); - let terms = linear_decoded_terms(&lower); - assert_eq!(terms.len(), 16 * 16); - assert_eq!( - find_term(terms, PackingFamilyId::UnsignedIncChunk { index: 0 }, 0, 7).coefficient, - Fr::from_u64(7) - ); - assert_eq!( - find_term(terms, PackingFamilyId::UnsignedIncChunk { index: 1 }, 0, 3).coefficient, - Fr::from_u64(16 * 3) - ); - assert_eq!( - find_term(terms, PackingFamilyId::UnsignedIncChunk { index: 15 }, 0, 2).coefficient, - Fr::from_u64(1u64 << 60) * Fr::from_u64(2) - ); - } - - #[test] - fn unsigned_inc_validity_requirements_cover_chunks_and_msb() { - let requirements = - unsigned_inc_validity_requirements(4).expect("4-bit chunking should be valid"); - - assert_eq!(requirements.len(), 17); - assert_eq!( - requirements[0], - PackingValidityRequirement::exact_one_hot( - PackingFamilyId::UnsignedIncChunk { index: 0 }, - 1, - 16, - ) - ); - assert_eq!( - requirements[15], - PackingValidityRequirement::exact_one_hot( - PackingFamilyId::UnsignedIncChunk { index: 15 }, - 1, - 16, - ) - ); - assert_eq!( - requirements[16], - PackingValidityRequirement::boolean_indicator(PackingFamilyId::UnsignedIncMsb, 1, 2, 1,) - ); - } - - #[test] - fn packed_validity_digest_is_order_stable_and_kind_sensitive() { - let exact = PackingValidityRequirement::exact_one_hot( - PackingFamilyId::UnsignedIncChunk { index: 0 }, - 1, - 256, - ); - let msb = - PackingValidityRequirement::boolean_indicator(PackingFamilyId::UnsignedIncMsb, 1, 2, 1); - let optional = PackingValidityRequirement::optional_one_hot( - PackingFamilyId::UnsignedIncChunk { index: 0 }, - 1, - 256, - ); - - assert_eq!( - packing_validity_digest(&[exact.clone(), msb.clone()]), - packing_validity_digest(&[msb, exact.clone()]) - ); - assert_ne!( - packing_validity_digest(&[exact]), - packing_validity_digest(&[optional]) - ); - } - - #[test] - fn bytecode_validity_requirements_cover_committed_program_facts() { - let chunk = 2; - let field_byte_width = 16; - let requirements = bytecode_validity_requirements(chunk, field_byte_width); - - assert_eq!( - requirements.len(), - 3 + NUM_CIRCUIT_FLAGS + NUM_INSTRUCTION_FLAGS + 5 - ); - assert!( - requirements.contains(&PackingValidityRequirement::optional_one_hot( - PackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }, - 1, - 1 << REGISTER_ADDRESS_BITS, - )) - ); - assert!( - requirements.contains(&PackingValidityRequirement::boolean_indicator( - PackingFamilyId::BytecodeCircuitFlag { chunk, flag: 0 }, - 1, - 2, - 1, - )) - ); - assert!( - requirements.contains(&PackingValidityRequirement::boolean_indicator( - PackingFamilyId::BytecodeInstructionFlag { - chunk, - flag: NUM_INSTRUCTION_FLAGS - 1, - }, - 1, - 2, - 1, - )) - ); - assert!( - requirements.contains(&PackingValidityRequirement::optional_one_hot( - PackingFamilyId::BytecodeLookupSelector { chunk }, - 1, - LookupTableKind::::COUNT.next_power_of_two(), - )) - ); - assert!( - requirements.contains(&PackingValidityRequirement::boolean_indicator( - PackingFamilyId::BytecodeRafFlag { chunk }, - 1, - 2, - 1, - )) - ); - assert!( - requirements.contains(&PackingValidityRequirement::exact_one_hot( - PackingFamilyId::BytecodeUnexpandedPcBytes { chunk }, - 8, - 256, - )) - ); - assert!( - requirements.contains(&PackingValidityRequirement::exact_one_hot( - PackingFamilyId::BytecodeImmBytes { chunk }, - field_byte_width, - 256, - )) - ); - assert!( - requirements.contains(&PackingValidityRequirement::bytecode_store_rd_disjoint( - chunk, - CircuitFlags::Store as usize - )) - ); - } - - #[test] - fn bytecode_imm_canonical_bytes_requirement_anchors_bytecode_immediates() { - assert_eq!( - bytecode_imm_canonical_bytes_requirement(2, 16, 97), - PackingValidityRequirement::field_element_canonical_bytes( - PackingFamilyId::BytecodeImmBytes { chunk: 2 }, - 16, - 97, - ) - ); - } - - #[test] - fn advice_and_program_image_validity_requirements_are_byte_facts() { - assert_eq!( - advice_bytes_validity_requirement(JoltAdviceKind::Trusted), - PackingValidityRequirement::exact_one_hot( - PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Trusted, - index: 0, - }, - 1, - 256, - ) - ); - assert_eq!( - program_image_validity_requirement(), - PackingValidityRequirement::exact_one_hot(PackingFamilyId::ProgramImageInit, 8, 256,) - ); - } - - #[test] - fn byte_decode_terms_are_little_endian_symbol_weights() { - let terms = byte_decode_terms::(PackingFamilyId::BytecodeChunk { index: 0 }, 3); - - assert_eq!(terms.len(), 256); - assert_eq!(terms[7].coefficient, Fr::from_u64(7)); - assert_eq!(terms[7].family, PackingFamilyId::BytecodeChunk { index: 0 }); - assert_eq!(terms[7].limb, 3); - assert_eq!(terms[7].symbol, 7); - } - - #[test] - fn committed_bytecode_lattice_family_ids_name_lane_classes() { - assert_ne!( - PackingFamilyId::BytecodeRegisterSelector { - chunk: 0, - selector: 0, - }, - PackingFamilyId::BytecodeRegisterSelector { - chunk: 0, - selector: 1, - } - ); - assert_ne!( - PackingFamilyId::BytecodeUnexpandedPcBytes { chunk: 0 }, - PackingFamilyId::BytecodeImmBytes { chunk: 0 } - ); - assert_ne!( - PackingFamilyId::BytecodeCircuitFlag { chunk: 0, flag: 0 }, - PackingFamilyId::BytecodeInstructionFlag { chunk: 0, flag: 0 } - ); - } - - #[test] - fn bytecode_chunk_lattice_view_formula_uses_cycle_major_lane_weights() { - let lane_vars = bytecode_reduction::committed_lane_vars(); - let log_bytecode = 2; - let lane_point = (1..=lane_vars as u64).map(Fr::from_u64).collect::>(); - let mut opening_point = lane_point.clone(); - opening_point.extend([Fr::from_u64(101), Fr::from_u64(103)]); - let lane_weights = EqPolynomial::::evals(&lane_point, None); - let lane_layout = bytecode_reduction::BYTECODE_LANE_LAYOUT; - - let formula = bytecode_chunk_lattice_view_formula( - 2, - &opening_point, - TracePolynomialOrder::CycleMajor, - log_bytecode, - 2, - ) - .unwrap(); - let terms = linear_decoded_terms(&formula); - - assert_eq!( - find_term( - terms, - PackingFamilyId::BytecodeRegisterSelector { - chunk: 2, - selector: 2 - }, - 0, - 5 - ) - .coefficient, - lane_weights[lane_layout.rd_start + 5] - ); - assert_eq!( - find_term( - terms, - PackingFamilyId::BytecodeCircuitFlag { chunk: 2, flag: 0 }, - 0, - 1 - ) - .coefficient, - lane_weights[lane_layout.circuit_start] - ); - assert_eq!( - find_term( - terms, - PackingFamilyId::BytecodeLookupSelector { chunk: 2 }, - 0, - 3 - ) - .coefficient, - lane_weights[lane_layout.lookup_start + 3] - ); - assert_eq!( - find_term( - terms, - PackingFamilyId::BytecodeUnexpandedPcBytes { chunk: 2 }, - 1, - 7 - ) - .coefficient, - lane_weights[lane_layout.unexp_pc_idx] * Fr::from_u64(256 * 7) - ); - assert_eq!( - find_term(terms, PackingFamilyId::BytecodeImmBytes { chunk: 2 }, 1, 9).coefficient, - lane_weights[lane_layout.imm_idx] * Fr::from_u64(256 * 9) - ); - assert_eq!( - find_term(terms, PackingFamilyId::BytecodeRafFlag { chunk: 2 }, 0, 1).coefficient, - lane_weights[lane_layout.raf_flag_idx] - ); - } - - #[test] - fn bytecode_chunk_lattice_view_formula_uses_address_major_lane_suffix() { - let lane_vars = bytecode_reduction::committed_lane_vars(); - let log_bytecode = 2; - let lane_point = (11..11 + lane_vars as u64) - .map(Fr::from_u64) - .collect::>(); - let mut opening_point = vec![Fr::from_u64(101), Fr::from_u64(103)]; - opening_point.extend(lane_point.iter().copied()); - let lane_weights = EqPolynomial::::evals(&lane_point, None); - - let formula = bytecode_chunk_lattice_view_formula( - 1, - &opening_point, - TracePolynomialOrder::AddressMajor, - log_bytecode, - 1, - ) - .unwrap(); - let terms = linear_decoded_terms(&formula); - - assert_eq!( - find_term( - terms, - PackingFamilyId::BytecodeRegisterSelector { - chunk: 1, - selector: 0 - }, - 0, - 1 - ) - .coefficient, - lane_weights[bytecode_reduction::BYTECODE_LANE_LAYOUT.rs1_start + 1] - ); - } - - #[test] - fn bytecode_chunk_lattice_view_formula_rejects_bad_point_length() { - let expected = bytecode_reduction::committed_lane_vars() + 3; - - let err = bytecode_chunk_lattice_view_formula::( - 0, - &vec![Fr::from_u64(0); expected - 1], - TracePolynomialOrder::CycleMajor, - 3, - 1, - ) - .unwrap_err(); - - assert_eq!( - err, - JoltFormulaPointError::OpeningPointLengthMismatch { - expected, - got: expected - 1 - } - ); - } - - #[test] - fn symbol_decode_terms_support_non_byte_alphabets() { - let terms = symbol_decode_terms::(PackingFamilyId::RamRa { index: 1 }, 0, 4); - - assert_eq!(terms.len(), 4); - assert_eq!(terms[3].coefficient, Fr::from_u64(3)); - assert_eq!(terms[3].family, PackingFamilyId::RamRa { index: 1 }); - assert_eq!(terms[3].limb, 0); - assert_eq!(terms[3].symbol, 3); - } - - #[test] - fn weighted_symbol_terms_use_supplied_coefficients() { - let terms = weighted_symbol_terms( - PackingFamilyId::InstructionRa { index: 0 }, - 2, - [Fr::from_u64(11), Fr::from_u64(13), Fr::from_u64(17)], - ); - - assert_eq!(terms.len(), 3); - assert_eq!(terms[1].coefficient, Fr::from_u64(13)); - assert_eq!(terms[1].family, PackingFamilyId::InstructionRa { index: 0 }); - assert_eq!(terms[1].limb, 2); - assert_eq!(terms[1].symbol, 1); - } - - #[test] - fn weighted_byte_decode_terms_scale_symbols_by_limb_weights() { - let terms = weighted_byte_decode_terms( - PackingFamilyId::BytecodeChunk { index: 2 }, - [(3, Fr::from_u64(5)), (8, Fr::from_u64(7))], - ); - - assert_eq!(terms.len(), 512); - assert_eq!(terms[9].coefficient, Fr::from_u64(45)); - assert_eq!(terms[9].limb, 3); - assert_eq!(terms[9].symbol, 9); - assert_eq!(terms[256 + 9].coefficient, Fr::from_u64(63)); - assert_eq!(terms[256 + 9].limb, 8); - assert_eq!( - terms[256 + 9].family, - PackingFamilyId::BytecodeChunk { index: 2 } - ); - } - - #[test] - fn little_endian_byte_decode_terms_weight_limbs_by_place_value() { - let terms = little_endian_byte_decode_terms::(PackingFamilyId::ProgramImageInit, 2); - - assert_eq!(terms.len(), 512); - assert_eq!(terms[7].coefficient, Fr::from_u64(7)); - assert_eq!(terms[7].limb, 0); - assert_eq!(terms[7].symbol, 7); - assert_eq!(terms[256 + 7].coefficient, Fr::from_u64(256 * 7)); - assert_eq!(terms[256 + 7].limb, 1); - assert_eq!(terms[256 + 7].symbol, 7); - assert_eq!(terms[256 + 7].family, PackingFamilyId::ProgramImageInit); - } - - fn linear_decoded_terms(formula: &PackingViewFormula) -> &[PackingViewTerm] { - match formula { - PackingViewFormula::LinearDecoded { terms, .. } => terms, - _ => panic!("expected linear decoded formula"), - } - } - - fn find_term( - terms: &[PackingViewTerm], - family: PackingFamilyId, - limb: usize, - symbol: usize, - ) -> &PackingViewTerm { - terms - .iter() - .find(|term| term.family == family && term.limb == limb && term.symbol == symbol) - .unwrap() - } -} +pub use families::{jolt_advice_kind, packing_advice_kind, JoltPackingFamilyId}; +pub use layout::{ + derive_jolt_lattice_packed_validity_requirements, derive_jolt_lattice_packed_witness_layout, + lattice_validity_requirements_for_packed_witness_layout, layout_has_advice, + layout_has_field_rd_inc, packed_alphabet_with_size, packed_family_is_precommitted, + FieldRdIncPacking, JoltLatticeLayoutError, JoltLatticePackingInputs, JoltLatticeValidityInputs, +}; +pub use openings::{ + final_opening_lattice_requirement, inc_virtualization_inc_opening, + inc_virtualization_input_openings, inc_virtualization_output_openings, + inc_virtualization_ram_read_write_opening, inc_virtualization_ram_val_check_opening, + inc_virtualization_rd_read_write_opening, inc_virtualization_rd_val_evaluation_opening, + inc_virtualization_relation, inc_virtualization_store_opening, unsigned_inc_chunk_opening, + unsigned_inc_chunk_reconstruction_relation, unsigned_inc_claim_reduction_relation, + unsigned_inc_input_opening, unsigned_inc_lower_chunk_count, unsigned_inc_msb_opening, + unsigned_inc_opening, LatticeFinalOpeningRequirement, +}; +pub use relations::{ + inc_virtualization_claim, unsigned_inc_chunk_reconstruction_claim, + unsigned_inc_claim_reduction_claim, unsigned_inc_msb_booleanity_claim, +}; +pub use validity::{ + advice_bytes_validity_requirement, bytecode_imm_canonical_bytes_requirement, + bytecode_validity_requirements, program_image_validity_requirement, + unsigned_inc_validity_requirements, +}; +pub use views::{ + byte_decode_terms, bytecode_chunk_lattice_view_formula, + bytecode_rd_present_lattice_view_formula, bytecode_store_flag_lattice_view_formula, + little_endian_byte_decode_terms, symbol_decode_terms, + unsigned_inc_lower_value_lattice_view_formula, unsigned_inc_lower_value_terms, + unsigned_inc_msb_lattice_view_formula, weighted_byte_decode_terms, weighted_symbol_terms, +}; diff --git a/crates/jolt-claims/src/protocols/jolt/formulas/lattice/families.rs b/crates/jolt-claims/src/protocols/jolt/formulas/lattice/families.rs new file mode 100644 index 0000000000..4079e0e94e --- /dev/null +++ b/crates/jolt-claims/src/protocols/jolt/formulas/lattice/families.rs @@ -0,0 +1,181 @@ +use jolt_openings::{PackingAdviceKind, PackingFamilyId}; +use serde::{Deserialize, Serialize}; + +use crate::protocols::jolt::JoltAdviceKind; + +const JOLT_PACKING_FAMILY_NAMESPACE: u64 = 0x6a6f_6c74_7063_7301; +const BYTECODE_REGISTER_SELECTOR_ID: u64 = 15; +const BYTECODE_CIRCUIT_FLAG_ID: u64 = 16; +const BYTECODE_INSTRUCTION_FLAG_ID: u64 = 17; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub enum JoltPackingFamilyId { + InstructionRa { index: usize }, + BytecodeRa { index: usize }, + RamRa { index: usize }, + UnsignedIncChunk { index: usize }, + UnsignedIncMsb, + FieldRdIncByte { index: usize }, + FieldRdIncSign, + AdviceBytes { kind: JoltAdviceKind, index: usize }, + BytecodeChunk { index: usize }, + BytecodeRegisterSelector { chunk: usize, selector: usize }, + BytecodeCircuitFlag { chunk: usize, flag: usize }, + BytecodeInstructionFlag { chunk: usize, flag: usize }, + BytecodeLookupSelector { chunk: usize }, + BytecodeRafFlag { chunk: usize }, + BytecodeUnexpandedPcBytes { chunk: usize }, + BytecodeImmBytes { chunk: usize }, + ProgramImageInit, +} + +impl JoltPackingFamilyId { + pub fn physical_id(self) -> PackingFamilyId { + let (id, index) = self.physical_parts(); + PackingFamilyId::new(JOLT_PACKING_FAMILY_NAMESPACE, id, index) + } + + pub fn from_physical_id(family: &PackingFamilyId) -> Option { + if family.namespace != JOLT_PACKING_FAMILY_NAMESPACE { + return None; + } + let index = usize::try_from(family.index).ok()?; + match family.id { + 0 => Some(Self::InstructionRa { index }), + 1 => Some(Self::BytecodeRa { index }), + 2 => Some(Self::RamRa { index }), + 3 => Some(Self::UnsignedIncChunk { index }), + 4 if family.index == 0 => Some(Self::UnsignedIncMsb), + 9 => Some(Self::FieldRdIncByte { index }), + 10 if family.index == 0 => Some(Self::FieldRdIncSign), + 11 => Some(Self::AdviceBytes { + kind: JoltAdviceKind::Trusted, + index, + }), + 12 => Some(Self::AdviceBytes { + kind: JoltAdviceKind::Untrusted, + index, + }), + 13 => Some(Self::BytecodeChunk { index }), + 14 if family.index == 0 => Some(Self::ProgramImageInit), + BYTECODE_REGISTER_SELECTOR_ID => { + let (chunk, selector) = split_two_indices(family.index)?; + Some(Self::BytecodeRegisterSelector { chunk, selector }) + } + BYTECODE_CIRCUIT_FLAG_ID => { + let (chunk, flag) = split_two_indices(family.index)?; + Some(Self::BytecodeCircuitFlag { chunk, flag }) + } + BYTECODE_INSTRUCTION_FLAG_ID => { + let (chunk, flag) = split_two_indices(family.index)?; + Some(Self::BytecodeInstructionFlag { chunk, flag }) + } + 18 => Some(Self::BytecodeLookupSelector { chunk: index }), + 19 => Some(Self::BytecodeRafFlag { chunk: index }), + 20 => Some(Self::BytecodeUnexpandedPcBytes { chunk: index }), + 21 => Some(Self::BytecodeImmBytes { chunk: index }), + _ => None, + } + } + + #[expect( + clippy::expect_used, + reason = "Jolt bytecode chunk and lane indices are protocol-sized and must fit in u32" + )] + fn physical_parts(self) -> (u64, u64) { + match self { + Self::InstructionRa { index } => (0, index as u64), + Self::BytecodeRa { index } => (1, index as u64), + Self::RamRa { index } => (2, index as u64), + Self::UnsignedIncChunk { index } => (3, index as u64), + Self::UnsignedIncMsb => (4, 0), + Self::FieldRdIncByte { index } => (9, index as u64), + Self::FieldRdIncSign => (10, 0), + Self::AdviceBytes { kind, index } => match kind { + JoltAdviceKind::Trusted => (11, index as u64), + JoltAdviceKind::Untrusted => (12, index as u64), + }, + Self::BytecodeChunk { index } => (13, index as u64), + Self::ProgramImageInit => (14, 0), + Self::BytecodeRegisterSelector { chunk, selector } => ( + BYTECODE_REGISTER_SELECTOR_ID, + combine_two_indices(chunk, selector).expect("bytecode selector index fits in u32"), + ), + Self::BytecodeCircuitFlag { chunk, flag } => ( + BYTECODE_CIRCUIT_FLAG_ID, + combine_two_indices(chunk, flag).expect("bytecode circuit flag index fits in u32"), + ), + Self::BytecodeInstructionFlag { chunk, flag } => ( + BYTECODE_INSTRUCTION_FLAG_ID, + combine_two_indices(chunk, flag) + .expect("bytecode instruction flag index fits in u32"), + ), + Self::BytecodeLookupSelector { chunk } => (18, chunk as u64), + Self::BytecodeRafFlag { chunk } => (19, chunk as u64), + Self::BytecodeUnexpandedPcBytes { chunk } => (20, chunk as u64), + Self::BytecodeImmBytes { chunk } => (21, chunk as u64), + } + } +} + +fn combine_two_indices(left: usize, right: usize) -> Option { + let left = u64::from(u32::try_from(left).ok()?); + let right = u64::from(u32::try_from(right).ok()?); + Some((left << 32) | right) +} + +fn split_two_indices(value: u64) -> Option<(usize, usize)> { + let left = usize::try_from(value >> 32).ok()?; + let right = usize::try_from(value & u64::from(u32::MAX)).ok()?; + Some((left, right)) +} + +impl From for PackingFamilyId { + fn from(family: JoltPackingFamilyId) -> Self { + family.physical_id() + } +} + +pub fn packing_advice_kind(kind: JoltAdviceKind) -> PackingAdviceKind { + match kind { + JoltAdviceKind::Trusted => PackingAdviceKind::Trusted, + JoltAdviceKind::Untrusted => PackingAdviceKind::Untrusted, + } +} + +pub fn jolt_advice_kind(kind: PackingAdviceKind) -> JoltAdviceKind { + match kind { + PackingAdviceKind::Trusted => JoltAdviceKind::Trusted, + PackingAdviceKind::Untrusted => JoltAdviceKind::Untrusted, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bytecode_subfamilies_round_trip_checked_composite_indices() { + let family = JoltPackingFamilyId::BytecodeRegisterSelector { + chunk: 7, + selector: 5, + }; + let physical = family.physical_id(); + assert_eq!(physical.namespace, JOLT_PACKING_FAMILY_NAMESPACE); + assert_eq!(physical.id, BYTECODE_REGISTER_SELECTOR_ID); + assert_eq!(physical.index, (7_u64 << 32) | 5); + assert_eq!( + JoltPackingFamilyId::from_physical_id(&physical), + Some(family) + ); + + assert_eq!( + JoltPackingFamilyId::from_physical_id(&PackingFamilyId::new( + JOLT_PACKING_FAMILY_NAMESPACE, + 99, + 0, + )), + None + ); + } +} diff --git a/crates/jolt-claims/src/protocols/jolt/formulas/lattice/layout.rs b/crates/jolt-claims/src/protocols/jolt/formulas/lattice/layout.rs new file mode 100644 index 0000000000..979298322a --- /dev/null +++ b/crates/jolt-claims/src/protocols/jolt/formulas/lattice/layout.rs @@ -0,0 +1,385 @@ +use jolt_openings::{ + PackingAdviceKind, PackingAlphabet, PackingFactDomain, PackingFamilyId, PackingFamilySpec, + PackingLayoutError, PackingValidityKind, PackingValidityRequirement, PackingWitnessLayout, +}; +use jolt_riscv::CircuitFlags; +use thiserror::Error; + +use crate::protocols::field_inline::formulas::lattice as field_lattice; +use crate::protocols::jolt::{AdviceClaimReductionLayout, JoltAdviceKind}; + +use super::super::ra::JoltRaPolynomialLayout; +use super::families::{packing_advice_kind, JoltPackingFamilyId}; +use super::{advice_bytes_validity_requirement, unsigned_inc_validity_requirements}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct FieldRdIncPacking { + pub byte_width: usize, + pub canonical_modulus: Option, +} + +#[derive(Clone, Copy, Debug)] +pub struct JoltLatticePackingInputs<'a> { + pub log_t: usize, + pub log_k_chunk: usize, + pub ra_layout: JoltRaPolynomialLayout, + pub field_rd_inc: Option, + pub untrusted_advice: Option<&'a AdviceClaimReductionLayout>, +} + +#[derive(Clone, Copy, Debug)] +pub struct JoltLatticeValidityInputs<'a> { + pub log_k_chunk: usize, + pub field_rd_inc: Option, + pub untrusted_advice: Option<&'a AdviceClaimReductionLayout>, +} + +impl<'a> From> for JoltLatticeValidityInputs<'a> { + fn from(inputs: JoltLatticePackingInputs<'a>) -> Self { + Self { + log_k_chunk: inputs.log_k_chunk, + field_rd_inc: inputs.field_rd_inc, + untrusted_advice: inputs.untrusted_advice, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Error)] +pub enum JoltLatticeLayoutError { + #[error( + "unsigned increment chunk reconstruction requires log_k_chunk to divide 64, got {log_k_chunk}" + )] + InvalidUnsignedIncChunking { log_k_chunk: usize }, + #[error("lattice one-hot chunk size must be nonzero")] + ZeroOneHotChunkSize, + #[error("lattice one-hot chunk size {log_k_chunk} is too large")] + OneHotChunkSizeTooLarge { log_k_chunk: usize }, + #[error("packed validity requirement alphabet size must be nonzero")] + ZeroAlphabetSize, + #[error("packed witness family {family:?} is not a Jolt lattice family")] + UnsupportedPackingFamily { family: PackingFamilyId }, + #[error(transparent)] + PackingLayout(#[from] PackingLayoutError), +} + +pub fn derive_jolt_lattice_packed_witness_layout( + inputs: JoltLatticePackingInputs<'_>, +) -> Result { + let trace = PackingFactDomain::TraceRows { + log_t: inputs.log_t, + }; + let ra_alphabet = one_hot_alphabet(inputs.log_k_chunk)?; + let mut specs = Vec::new(); + specs.extend((0..inputs.ra_layout.instruction()).map(|index| { + PackingFamilySpec::direct( + JoltPackingFamilyId::InstructionRa { index }.into(), + trace, + 1, + ra_alphabet, + ) + })); + specs.extend((0..inputs.ra_layout.bytecode()).map(|index| { + PackingFamilySpec::direct( + JoltPackingFamilyId::BytecodeRa { index }.into(), + trace, + 1, + ra_alphabet, + ) + })); + specs.extend((0..inputs.ra_layout.ram()).map(|index| { + PackingFamilySpec::direct( + JoltPackingFamilyId::RamRa { index }.into(), + trace, + 1, + ra_alphabet, + ) + })); + + let unsigned_inc_requirements = unsigned_inc_validity_requirements(inputs.log_k_chunk).ok_or( + JoltLatticeLayoutError::InvalidUnsignedIncChunking { + log_k_chunk: inputs.log_k_chunk, + }, + )?; + extend_validity_requirement_families(&mut specs, &unsigned_inc_requirements, trace)?; + + if let Some(field_rd_inc) = inputs.field_rd_inc { + extend_validity_requirement_families( + &mut specs, + &field_rd_inc_validity_requirements(field_rd_inc), + trace, + )?; + } + + if let Some(layout) = inputs.untrusted_advice { + specs.push(advice_family(JoltAdviceKind::Untrusted, layout)?); + } + + Ok(PackingWitnessLayout::new(specs)?) +} + +pub fn derive_jolt_lattice_packed_validity_requirements( + inputs: JoltLatticeValidityInputs<'_>, +) -> Result, JoltLatticeLayoutError> { + let mut requirements = unsigned_inc_validity_requirements(inputs.log_k_chunk).ok_or( + JoltLatticeLayoutError::InvalidUnsignedIncChunking { + log_k_chunk: inputs.log_k_chunk, + }, + )?; + if let Some(field_rd_inc) = inputs.field_rd_inc { + requirements.extend(field_rd_inc_validity_requirements(field_rd_inc)); + } + if inputs.untrusted_advice.is_some() { + requirements.push(advice_bytes_validity_requirement(JoltAdviceKind::Untrusted)); + } + Ok(requirements) +} + +pub fn lattice_validity_requirements_for_packed_witness_layout( + layout: &PackingWitnessLayout, +) -> Result, JoltLatticeLayoutError> { + let mut requirements = Vec::new(); + for family in &layout.families { + let limbs = family.limbs; + let alphabet_size = family.alphabet.size(); + let jolt_family = JoltPackingFamilyId::from_physical_id(&family.id) + .ok_or(JoltLatticeLayoutError::UnsupportedPackingFamily { family: family.id })?; + let requirement = match jolt_family { + JoltPackingFamilyId::UnsignedIncChunk { index } => { + Some(PackingValidityRequirement::exact_one_hot( + JoltPackingFamilyId::UnsignedIncChunk { index }.into(), + limbs, + alphabet_size, + )) + } + JoltPackingFamilyId::UnsignedIncMsb => { + Some(PackingValidityRequirement::boolean_indicator( + JoltPackingFamilyId::UnsignedIncMsb.into(), + limbs, + alphabet_size, + 1, + )) + } + JoltPackingFamilyId::FieldRdIncByte { index } => { + Some(PackingValidityRequirement::exact_one_hot( + JoltPackingFamilyId::FieldRdIncByte { index }.into(), + limbs, + alphabet_size, + )) + } + JoltPackingFamilyId::AdviceBytes { kind, index } => { + Some(PackingValidityRequirement::exact_one_hot( + JoltPackingFamilyId::AdviceBytes { kind, index }.into(), + limbs, + alphabet_size, + )) + } + JoltPackingFamilyId::BytecodeRegisterSelector { chunk, selector } => { + Some(PackingValidityRequirement::optional_one_hot( + JoltPackingFamilyId::BytecodeRegisterSelector { chunk, selector }.into(), + limbs, + alphabet_size, + )) + } + JoltPackingFamilyId::BytecodeCircuitFlag { chunk, flag } => { + Some(PackingValidityRequirement::boolean_indicator( + JoltPackingFamilyId::BytecodeCircuitFlag { chunk, flag }.into(), + limbs, + alphabet_size, + 1, + )) + } + JoltPackingFamilyId::BytecodeInstructionFlag { chunk, flag } => { + Some(PackingValidityRequirement::boolean_indicator( + JoltPackingFamilyId::BytecodeInstructionFlag { chunk, flag }.into(), + limbs, + alphabet_size, + 1, + )) + } + JoltPackingFamilyId::BytecodeLookupSelector { chunk } => { + Some(PackingValidityRequirement::optional_one_hot( + JoltPackingFamilyId::BytecodeLookupSelector { chunk }.into(), + limbs, + alphabet_size, + )) + } + JoltPackingFamilyId::BytecodeRafFlag { chunk } => { + Some(PackingValidityRequirement::boolean_indicator( + JoltPackingFamilyId::BytecodeRafFlag { chunk }.into(), + limbs, + alphabet_size, + 1, + )) + } + JoltPackingFamilyId::BytecodeUnexpandedPcBytes { chunk } => { + Some(PackingValidityRequirement::exact_one_hot( + JoltPackingFamilyId::BytecodeUnexpandedPcBytes { chunk }.into(), + limbs, + alphabet_size, + )) + } + JoltPackingFamilyId::BytecodeImmBytes { chunk } => { + Some(PackingValidityRequirement::exact_one_hot( + JoltPackingFamilyId::BytecodeImmBytes { chunk }.into(), + limbs, + alphabet_size, + )) + } + JoltPackingFamilyId::ProgramImageInit => { + Some(PackingValidityRequirement::exact_one_hot( + JoltPackingFamilyId::ProgramImageInit.into(), + limbs, + alphabet_size, + )) + } + JoltPackingFamilyId::InstructionRa { .. } + | JoltPackingFamilyId::BytecodeRa { .. } + | JoltPackingFamilyId::RamRa { .. } + | JoltPackingFamilyId::FieldRdIncSign + | JoltPackingFamilyId::BytecodeChunk { .. } => None, + }; + if let Some(requirement) = requirement { + requirements.push(requirement); + } + } + + for family in &layout.families { + let jolt_family = JoltPackingFamilyId::from_physical_id(&family.id) + .ok_or(JoltLatticeLayoutError::UnsupportedPackingFamily { family: family.id })?; + if let JoltPackingFamilyId::BytecodeCircuitFlag { chunk, flag } = jolt_family { + if flag == CircuitFlags::Store as usize + && layout + .family( + &JoltPackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 } + .into(), + ) + .is_some() + { + requirements.push(PackingValidityRequirement::bytecode_store_rd_disjoint( + JoltPackingFamilyId::BytecodeCircuitFlag { + chunk, + flag: CircuitFlags::Store as usize, + } + .into(), + )); + } + } + } + Ok(requirements) +} + +pub fn packed_family_is_precommitted(family: &PackingFamilyId) -> bool { + matches!( + JoltPackingFamilyId::from_physical_id(family), + Some( + JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Trusted, + .. + } | JoltPackingFamilyId::BytecodeChunk { .. } + | JoltPackingFamilyId::BytecodeRegisterSelector { .. } + | JoltPackingFamilyId::BytecodeCircuitFlag { .. } + | JoltPackingFamilyId::BytecodeInstructionFlag { .. } + | JoltPackingFamilyId::BytecodeLookupSelector { .. } + | JoltPackingFamilyId::BytecodeRafFlag { .. } + | JoltPackingFamilyId::BytecodeUnexpandedPcBytes { .. } + | JoltPackingFamilyId::BytecodeImmBytes { .. } + | JoltPackingFamilyId::ProgramImageInit + ) + ) +} + +pub fn layout_has_field_rd_inc(layout: &PackingWitnessLayout) -> bool { + layout.families.iter().any(|family| { + matches!( + JoltPackingFamilyId::from_physical_id(&family.id), + Some(JoltPackingFamilyId::FieldRdIncByte { .. }) + ) + }) +} + +pub fn layout_has_advice(layout: &PackingWitnessLayout, kind: PackingAdviceKind) -> bool { + layout.families.iter().any(|family| { + matches!( + JoltPackingFamilyId::from_physical_id(&family.id), + Some(JoltPackingFamilyId::AdviceBytes { + kind: family_kind, + .. + }) if packing_advice_kind(family_kind) == kind + ) + }) +} + +pub fn packed_alphabet_with_size(size: usize) -> Result { + match size { + 0 => Err(JoltLatticeLayoutError::ZeroAlphabetSize), + 2 => Ok(PackingAlphabet::Bit), + 256 => Ok(PackingAlphabet::Byte), + size => Ok(PackingAlphabet::Fixed { size }), + } +} + +fn advice_family( + kind: JoltAdviceKind, + layout: &AdviceClaimReductionLayout, +) -> Result { + let packed_kind = packing_advice_kind(kind); + let requirement = advice_bytes_validity_requirement(kind); + Ok(PackingFamilySpec::direct( + requirement.family, + PackingFactDomain::AdviceBytes { + kind: packed_kind, + log_bytes: layout.advice_shape().total_vars() + 3, + }, + requirement.limbs, + packed_alphabet_with_size(requirement.alphabet_size)?, + )) +} + +fn extend_validity_requirement_families( + specs: &mut Vec, + requirements: &[PackingValidityRequirement], + domain: PackingFactDomain, +) -> Result<(), JoltLatticeLayoutError> { + for requirement in requirements { + if matches!( + requirement.kind, + PackingValidityKind::BytecodeStoreRdDisjoint + | PackingValidityKind::FieldElementCanonicalBytes { .. } + ) { + continue; + } + specs.push(PackingFamilySpec::direct( + requirement.family, + domain, + requirement.limbs, + packed_alphabet_with_size(requirement.alphabet_size)?, + )); + } + Ok(()) +} + +fn field_rd_inc_validity_requirements( + field_rd_inc: FieldRdIncPacking, +) -> Vec { + let mut requirements = + field_lattice::field_rd_inc_validity_requirements(field_rd_inc.byte_width); + if let Some(modulus) = field_rd_inc.canonical_modulus { + requirements.push(field_lattice::field_rd_inc_canonical_bytes_requirement( + field_rd_inc.byte_width, + modulus, + )); + } + requirements +} + +fn one_hot_alphabet(log_k_chunk: usize) -> Result { + match log_k_chunk { + 0 => Err(JoltLatticeLayoutError::ZeroOneHotChunkSize), + 1 => Ok(PackingAlphabet::Bit), + 8 => Ok(PackingAlphabet::Byte), + bits if bits < usize::BITS as usize => Ok(PackingAlphabet::Fixed { + size: 1usize << bits, + }), + _ => Err(JoltLatticeLayoutError::OneHotChunkSizeTooLarge { log_k_chunk }), + } +} diff --git a/crates/jolt-claims/src/protocols/jolt/formulas/lattice/openings.rs b/crates/jolt-claims/src/protocols/jolt/formulas/lattice/openings.rs new file mode 100644 index 0000000000..ba486b40d6 --- /dev/null +++ b/crates/jolt-claims/src/protocols/jolt/formulas/lattice/openings.rs @@ -0,0 +1,167 @@ +use serde::{Deserialize, Serialize}; + +use crate::protocols::jolt::{JoltCommittedPolynomial, JoltOpeningId, JoltRelationId}; + +use super::families::JoltPackingFamilyId; +use super::UNSIGNED_INC_BITS; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) struct UnsignedIncChunking { + pub chunk_count: usize, + pub alphabet_size: usize, + pub radix: u64, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum LatticeFinalOpeningRequirement { + PackingLayoutFamily { + family: JoltPackingFamilyId, + relation: JoltRelationId, + }, + LogicalOnly, +} + +pub fn inc_virtualization_relation() -> JoltRelationId { + JoltRelationId::IncVirtualization +} + +pub fn unsigned_inc_claim_reduction_relation() -> JoltRelationId { + JoltRelationId::UnsignedIncClaimReduction +} + +pub fn unsigned_inc_chunk_reconstruction_relation() -> JoltRelationId { + JoltRelationId::UnsignedIncChunkReconstruction +} + +pub fn inc_virtualization_input_openings() -> [JoltOpeningId; 4] { + [ + inc_virtualization_ram_read_write_opening(), + inc_virtualization_ram_val_check_opening(), + inc_virtualization_rd_read_write_opening(), + inc_virtualization_rd_val_evaluation_opening(), + ] +} + +pub fn inc_virtualization_output_openings() -> [JoltOpeningId; 2] { + [ + inc_virtualization_inc_opening(), + inc_virtualization_store_opening(), + ] +} + +pub fn inc_virtualization_ram_read_write_opening() -> JoltOpeningId { + JoltOpeningId::committed( + JoltCommittedPolynomial::RamInc, + JoltRelationId::RamReadWriteChecking, + ) +} + +pub fn inc_virtualization_ram_val_check_opening() -> JoltOpeningId { + JoltOpeningId::committed(JoltCommittedPolynomial::RamInc, JoltRelationId::RamValCheck) +} + +pub fn inc_virtualization_rd_read_write_opening() -> JoltOpeningId { + JoltOpeningId::committed( + JoltCommittedPolynomial::RdInc, + JoltRelationId::RegistersReadWriteChecking, + ) +} + +pub fn inc_virtualization_rd_val_evaluation_opening() -> JoltOpeningId { + JoltOpeningId::committed( + JoltCommittedPolynomial::RdInc, + JoltRelationId::RegistersValEvaluation, + ) +} + +pub fn inc_virtualization_inc_opening() -> JoltOpeningId { + JoltOpeningId::lattice(JoltRelationId::IncVirtualization, 0) +} + +pub fn inc_virtualization_store_opening() -> JoltOpeningId { + JoltOpeningId::lattice(JoltRelationId::IncVirtualization, 1) +} + +pub fn unsigned_inc_input_opening() -> JoltOpeningId { + inc_virtualization_inc_opening() +} + +pub fn unsigned_inc_opening() -> JoltOpeningId { + JoltOpeningId::lattice(JoltRelationId::UnsignedIncClaimReduction, 0) +} + +pub fn unsigned_inc_msb_opening() -> JoltOpeningId { + JoltOpeningId::lattice(JoltRelationId::UnsignedIncClaimReduction, 1) +} + +pub fn unsigned_inc_chunk_opening(index: usize) -> JoltOpeningId { + JoltOpeningId::lattice(JoltRelationId::UnsignedIncClaimReduction, 2 + index) +} + +pub fn unsigned_inc_lower_chunk_count(log_k_chunk: usize) -> Option { + Some(unsigned_inc_chunking(log_k_chunk)?.chunk_count) +} + +pub(crate) fn unsigned_inc_chunking(log_k_chunk: usize) -> Option { + if log_k_chunk != 0 && UNSIGNED_INC_BITS.is_multiple_of(log_k_chunk) { + let shift = u32::try_from(log_k_chunk).ok()?; + Some(UnsignedIncChunking { + chunk_count: UNSIGNED_INC_BITS / log_k_chunk, + alphabet_size: 1usize.checked_shl(shift)?, + radix: 1u64.checked_shl(shift)?, + }) + } else { + None + } +} + +pub fn final_opening_lattice_requirement( + polynomial: JoltCommittedPolynomial, +) -> LatticeFinalOpeningRequirement { + match polynomial { + JoltCommittedPolynomial::RdInc | JoltCommittedPolynomial::RamInc => { + LatticeFinalOpeningRequirement::LogicalOnly + } + JoltCommittedPolynomial::InstructionRa(index) => packed_family_requirement( + JoltPackingFamilyId::InstructionRa { index }, + JoltRelationId::HammingWeightClaimReduction, + ), + JoltCommittedPolynomial::BytecodeRa(index) => packed_family_requirement( + JoltPackingFamilyId::BytecodeRa { index }, + JoltRelationId::HammingWeightClaimReduction, + ), + JoltCommittedPolynomial::RamRa(index) => packed_family_requirement( + JoltPackingFamilyId::RamRa { index }, + JoltRelationId::HammingWeightClaimReduction, + ), + JoltCommittedPolynomial::TrustedAdvice => packed_family_requirement( + JoltPackingFamilyId::AdviceBytes { + kind: crate::protocols::jolt::JoltAdviceKind::Trusted, + index: 0, + }, + JoltRelationId::AdviceClaimReduction, + ), + JoltCommittedPolynomial::UntrustedAdvice => packed_family_requirement( + JoltPackingFamilyId::AdviceBytes { + kind: crate::protocols::jolt::JoltAdviceKind::Untrusted, + index: 0, + }, + JoltRelationId::AdviceClaimReduction, + ), + JoltCommittedPolynomial::BytecodeChunk(index) => packed_family_requirement( + JoltPackingFamilyId::BytecodeChunk { index }, + JoltRelationId::BytecodeClaimReduction, + ), + JoltCommittedPolynomial::ProgramImageInit => packed_family_requirement( + JoltPackingFamilyId::ProgramImageInit, + JoltRelationId::ProgramImageClaimReduction, + ), + } +} + +fn packed_family_requirement( + family: JoltPackingFamilyId, + relation: JoltRelationId, +) -> LatticeFinalOpeningRequirement { + LatticeFinalOpeningRequirement::PackingLayoutFamily { family, relation } +} diff --git a/crates/jolt-claims/src/protocols/jolt/formulas/lattice/relations.rs b/crates/jolt-claims/src/protocols/jolt/formulas/lattice/relations.rs new file mode 100644 index 0000000000..039a74d8ff --- /dev/null +++ b/crates/jolt-claims/src/protocols/jolt/formulas/lattice/relations.rs @@ -0,0 +1,152 @@ +use jolt_field::{FromPrimitiveInt, RingCore}; + +use crate::protocols::jolt::{ + IncVirtualizationChallenge, IncVirtualizationPublic, JoltChallengeId, JoltExpr, JoltPublicId, + JoltRelationClaims, JoltRelationId, UnsignedIncChunkReconstructionChallenge, + UnsignedIncChunkReconstructionPublic, +}; +use crate::{challenge, constant, opening, public}; + +use super::super::dimensions::{JoltSumcheckSpec, TraceDimensions}; +use super::openings::{ + inc_virtualization_inc_opening, inc_virtualization_ram_read_write_opening, + inc_virtualization_ram_val_check_opening, inc_virtualization_rd_read_write_opening, + inc_virtualization_rd_val_evaluation_opening, inc_virtualization_store_opening, + unsigned_inc_chunk_opening, unsigned_inc_chunking, unsigned_inc_msb_opening, + unsigned_inc_opening, +}; + +pub fn inc_virtualization_claim(dimensions: TraceDimensions) -> JoltRelationClaims +where + F: RingCore, +{ + let gamma = inc_virtualization_challenge(IncVirtualizationChallenge::Gamma); + + let input = opening(inc_virtualization_ram_read_write_opening()) + + gamma.clone() * opening(inc_virtualization_ram_val_check_opening()) + + gamma.clone().pow(2) * opening(inc_virtualization_rd_read_write_opening()) + + gamma.clone().pow(3) * opening(inc_virtualization_rd_val_evaluation_opening()); + + let ram_coeff = inc_virtualization_public(IncVirtualizationPublic::EqRamReadWrite) + + gamma.clone() * inc_virtualization_public(IncVirtualizationPublic::EqRamValCheck); + let gamma_2 = gamma.clone().pow(2); + let rd_coeff = inc_virtualization_public(IncVirtualizationPublic::EqRegistersReadWrite) + + gamma.clone() + * inc_virtualization_public(IncVirtualizationPublic::EqRegistersValEvaluation); + let store = opening(inc_virtualization_store_opening()); + let output = opening(inc_virtualization_inc_opening()) + * (ram_coeff * store.clone() + gamma_2 * rd_coeff * (JoltExpr::one() - store)); + + JoltRelationClaims::new( + JoltRelationId::IncVirtualization, + dimensions.sumcheck(3), + input, + output, + ) +} + +pub fn unsigned_inc_claim_reduction_claim(dimensions: TraceDimensions) -> JoltRelationClaims +where + F: RingCore + FromPrimitiveInt, +{ + let input = opening(inc_virtualization_inc_opening()) + constant(F::from_u128(1u128 << 64)); + let output = opening(unsigned_inc_opening()); + + JoltRelationClaims::new( + JoltRelationId::UnsignedIncClaimReduction, + dimensions.sumcheck(2), + input, + output, + ) +} + +pub fn unsigned_inc_msb_booleanity_claim(dimensions: TraceDimensions) -> JoltRelationClaims +where + F: RingCore, +{ + let msb = opening(unsigned_inc_msb_opening()); + JoltRelationClaims::new( + JoltRelationId::Booleanity, + dimensions.sumcheck(2), + JoltExpr::zero(), + msb.clone() * msb.clone() - msb, + ) +} + +pub fn unsigned_inc_chunk_reconstruction_claim( + log_k_chunk: usize, +) -> Option> +where + F: RingCore + FromPrimitiveInt, +{ + let chunking = unsigned_inc_chunking(log_k_chunk)?; + let gamma = + unsigned_inc_chunk_reconstruction_challenge(UnsignedIncChunkReconstructionChallenge::Gamma); + let eq_booleanity_address = unsigned_inc_chunk_reconstruction_public( + UnsignedIncChunkReconstructionPublic::EqBooleanityAddress, + ); + let identity_at_address = unsigned_inc_chunk_reconstruction_public( + UnsignedIncChunkReconstructionPublic::IdentityAtAddress, + ); + let delta = gamma.clone().pow(2 * chunking.chunk_count); + let lower_value = opening(unsigned_inc_opening()) + - constant(F::from_u128(1u128 << 64)) * opening(unsigned_inc_msb_opening()); + + let mut input = delta.clone() * lower_value; + let mut output = JoltExpr::zero(); + let mut place = F::one(); + for index in 0..chunking.chunk_count { + input = input + + gamma.clone().pow(2 * index) + + gamma.clone().pow(2 * index + 1) * opening(unsigned_inc_chunk_opening(index)); + let output_coeff = gamma.clone().pow(2 * index) + + gamma.clone().pow(2 * index + 1) * eq_booleanity_address.clone() + + delta.clone() * constant(place) * identity_at_address.clone(); + output = output + output_coeff * opening(unsigned_inc_chunk_opening(index)); + place *= F::from_u64(chunking.radix); + } + + Some( + JoltRelationClaims::new( + JoltRelationId::UnsignedIncChunkReconstruction, + JoltSumcheckSpec::boolean(log_k_chunk, 3), + input, + output, + ) + .with_input_challenges([JoltChallengeId::from( + UnsignedIncChunkReconstructionChallenge::Gamma, + )]), + ) +} + +fn inc_virtualization_challenge(id: IncVirtualizationChallenge) -> JoltExpr +where + F: RingCore, +{ + challenge(JoltChallengeId::from(id)) +} + +fn inc_virtualization_public(id: IncVirtualizationPublic) -> JoltExpr +where + F: RingCore, +{ + public(JoltPublicId::from(id)) +} + +fn unsigned_inc_chunk_reconstruction_challenge( + id: UnsignedIncChunkReconstructionChallenge, +) -> JoltExpr +where + F: RingCore, +{ + challenge(JoltChallengeId::from(id)) +} + +fn unsigned_inc_chunk_reconstruction_public( + id: UnsignedIncChunkReconstructionPublic, +) -> JoltExpr +where + F: RingCore, +{ + public(JoltPublicId::from(id)) +} diff --git a/crates/jolt-claims/src/protocols/jolt/formulas/lattice/tests.rs b/crates/jolt-claims/src/protocols/jolt/formulas/lattice/tests.rs new file mode 100644 index 0000000000..bb46c6ce0d --- /dev/null +++ b/crates/jolt-claims/src/protocols/jolt/formulas/lattice/tests.rs @@ -0,0 +1,1007 @@ +#![expect( + clippy::expect_used, + clippy::panic, + clippy::unwrap_used, + reason = "tests fail loudly on unexpected errors" +)] + +use jolt_field::{Fr, FromPrimitiveInt}; +use jolt_lookup_tables::{LookupTableKind, XLEN}; +use jolt_openings::{ + PackingAlphabet, PackingFactDomain, PackingFamilyId, PackingFamilySpec, PackingWitnessLayout, +}; +use jolt_poly::EqPolynomial; +use jolt_riscv::{CircuitFlags, NUM_CIRCUIT_FLAGS, NUM_INSTRUCTION_FLAGS}; + +use super::super::claim_reductions::bytecode as bytecode_reduction; +use super::super::claim_reductions::precommitted::PrecommittedClaimReduction; +use super::super::dimensions::{ + CommitmentMatrixShape, JoltFormulaPointError, JoltSumcheckSpec, TraceDimensions, + TracePolynomialOrder, REGISTER_ADDRESS_BITS, +}; +use super::super::ra::JoltRaPolynomialLayout; +use super::*; +use crate::protocols::jolt::{ + AdviceClaimReductionLayout, IncVirtualizationChallenge, IncVirtualizationPublic, + JoltAdviceKind, JoltChallengeId, JoltCommittedPolynomial, JoltPublicId, JoltRelationId, + UnsignedIncChunkReconstructionChallenge, UnsignedIncChunkReconstructionPublic, +}; + +fn physical(family: JoltPackingFamilyId) -> PackingFamilyId { + family.into() +} + +#[test] +fn final_opening_lattice_requirement_marks_increments_as_logical_only() { + assert_eq!( + final_opening_lattice_requirement(JoltCommittedPolynomial::RamInc), + LatticeFinalOpeningRequirement::LogicalOnly + ); + assert_eq!( + final_opening_lattice_requirement(JoltCommittedPolynomial::RdInc), + LatticeFinalOpeningRequirement::LogicalOnly + ); +} + +#[test] +fn final_opening_lattice_requirement_names_packed_families() { + assert_eq!( + final_opening_lattice_requirement(JoltCommittedPolynomial::InstructionRa(2)), + LatticeFinalOpeningRequirement::PackingLayoutFamily { + family: JoltPackingFamilyId::InstructionRa { index: 2 }, + relation: JoltRelationId::HammingWeightClaimReduction, + } + ); + assert_eq!( + final_opening_lattice_requirement(JoltCommittedPolynomial::ProgramImageInit), + LatticeFinalOpeningRequirement::PackingLayoutFamily { + family: JoltPackingFamilyId::ProgramImageInit, + relation: JoltRelationId::ProgramImageClaimReduction, + } + ); +} + +#[test] +fn inc_virtualization_claim_exposes_expected_dependencies() { + let claims = inc_virtualization_claim::(TraceDimensions::new(5)); + + assert_eq!(claims.id, JoltRelationId::IncVirtualization); + assert_eq!(claims.sumcheck, TraceDimensions::new(5).sumcheck(3)); + assert_eq!( + claims.input.required_openings, + inc_virtualization_input_openings() + ); + assert_eq!( + claims.output.required_openings, + inc_virtualization_output_openings() + ); + assert_eq!( + claims.required_challenges(), + vec![JoltChallengeId::from(IncVirtualizationChallenge::Gamma)] + ); + assert_eq!( + claims.required_publics(), + vec![ + JoltPublicId::from(IncVirtualizationPublic::EqRamReadWrite), + JoltPublicId::from(IncVirtualizationPublic::EqRamValCheck), + JoltPublicId::from(IncVirtualizationPublic::EqRegistersReadWrite), + JoltPublicId::from(IncVirtualizationPublic::EqRegistersValEvaluation), + ] + ); +} + +#[test] +fn inc_virtualization_claim_evaluates_store_selected_inc() { + let claims = inc_virtualization_claim::(TraceDimensions::new(5)); + let ram_rw = Fr::from_u64(3); + let ram_val = Fr::from_u64(5); + let rd_rw = Fr::from_u64(7); + let rd_val = Fr::from_u64(11); + let inc = Fr::from_u64(13); + let store = Fr::from_u64(0); + let eq_ram_rw = Fr::from_u64(17); + let eq_ram_val = Fr::from_u64(19); + let eq_rd_rw = Fr::from_u64(23); + let eq_rd_val = Fr::from_u64(29); + let gamma = Fr::from_u64(31); + let zero = Fr::from_u64(0); + + let input = claims.input.expression().evaluate( + |id| match *id { + id if id == inc_virtualization_ram_read_write_opening() => ram_rw, + id if id == inc_virtualization_ram_val_check_opening() => ram_val, + id if id == inc_virtualization_rd_read_write_opening() => rd_rw, + id if id == inc_virtualization_rd_val_evaluation_opening() => rd_val, + _ => zero, + }, + |id| match id { + JoltChallengeId::IncVirtualization(IncVirtualizationChallenge::Gamma) => gamma, + _ => zero, + }, + |_| zero, + ); + let output = claims.output.expression().evaluate( + |id| match *id { + id if id == inc_virtualization_inc_opening() => inc, + id if id == inc_virtualization_store_opening() => store, + _ => zero, + }, + |id| match id { + JoltChallengeId::IncVirtualization(IncVirtualizationChallenge::Gamma) => gamma, + _ => zero, + }, + |id| match *id { + JoltPublicId::IncVirtualization(IncVirtualizationPublic::EqRamReadWrite) => eq_ram_rw, + JoltPublicId::IncVirtualization(IncVirtualizationPublic::EqRamValCheck) => eq_ram_val, + JoltPublicId::IncVirtualization(IncVirtualizationPublic::EqRegistersReadWrite) => { + eq_rd_rw + } + JoltPublicId::IncVirtualization(IncVirtualizationPublic::EqRegistersValEvaluation) => { + eq_rd_val + } + _ => zero, + }, + ); + + let gamma_2 = gamma * gamma; + assert_eq!( + input, + ram_rw + gamma * ram_val + gamma_2 * rd_rw + gamma_2 * gamma * rd_val + ); + assert_eq!(output, inc * gamma_2 * (eq_rd_rw + gamma * eq_rd_val)); + + let store_output = claims.output.expression().evaluate( + |id| match *id { + id if id == inc_virtualization_inc_opening() => inc, + id if id == inc_virtualization_store_opening() => Fr::from_u64(1), + _ => zero, + }, + |id| match id { + JoltChallengeId::IncVirtualization(IncVirtualizationChallenge::Gamma) => gamma, + _ => zero, + }, + |id| match *id { + JoltPublicId::IncVirtualization(IncVirtualizationPublic::EqRamReadWrite) => eq_ram_rw, + JoltPublicId::IncVirtualization(IncVirtualizationPublic::EqRamValCheck) => eq_ram_val, + JoltPublicId::IncVirtualization(IncVirtualizationPublic::EqRegistersReadWrite) => { + eq_rd_rw + } + JoltPublicId::IncVirtualization(IncVirtualizationPublic::EqRegistersValEvaluation) => { + eq_rd_val + } + _ => zero, + }, + ); + assert_eq!(store_output, inc * (eq_ram_rw + gamma * eq_ram_val)); +} + +#[test] +fn unsigned_inc_claim_reduction_offsets_inc_by_two_to_64() { + let claims = unsigned_inc_claim_reduction_claim::(TraceDimensions::new(5)); + let inc = Fr::from_u64(13); + let unsigned_inc = Fr::from_u128((1u128 << 64) + 13); + let zero = Fr::from_u64(0); + + assert_eq!(claims.id, JoltRelationId::UnsignedIncClaimReduction); + assert_eq!(claims.sumcheck, TraceDimensions::new(5).sumcheck(2)); + assert_eq!( + claims.input.required_openings, + vec![inc_virtualization_inc_opening()] + ); + assert_eq!( + claims.output.required_openings, + vec![unsigned_inc_opening()] + ); + assert!(claims.required_challenges().is_empty()); + assert!(claims.required_publics().is_empty()); + + let input = claims.input.expression().evaluate( + |id| match *id { + id if id == inc_virtualization_inc_opening() => inc, + _ => zero, + }, + |_| zero, + |_| zero, + ); + let output = claims.output.expression().evaluate( + |id| match *id { + id if id == unsigned_inc_opening() => unsigned_inc, + _ => zero, + }, + |_| zero, + |_| zero, + ); + + assert_eq!(input, unsigned_inc); + assert_eq!(output, unsigned_inc); +} + +#[test] +fn unsigned_inc_msb_booleanity_claim_checks_cycle_bit() { + let claims = unsigned_inc_msb_booleanity_claim::(TraceDimensions::new(5)); + let zero = Fr::from_u64(0); + let msb = Fr::from_u64(7); + + assert_eq!(claims.id, JoltRelationId::Booleanity); + assert_eq!(claims.sumcheck, TraceDimensions::new(5).sumcheck(2)); + assert!(claims.input.required_openings.is_empty()); + assert_eq!( + claims.output.required_openings, + vec![unsigned_inc_msb_opening()] + ); + assert!(claims.required_challenges().is_empty()); + assert!(claims.required_publics().is_empty()); + + let output = claims.output.expression().evaluate( + |id| match *id { + id if id == unsigned_inc_msb_opening() => msb, + _ => zero, + }, + |_| zero, + |_| zero, + ); + + assert_eq!(output, msb * msb - msb); +} + +#[test] +fn unsigned_inc_chunk_reconstruction_claim_batches_hamming_point_and_value() { + let claims = + unsigned_inc_chunk_reconstruction_claim::(8).expect("8-bit chunking should be valid"); + + assert_eq!(claims.id, JoltRelationId::UnsignedIncChunkReconstruction); + assert_eq!(claims.sumcheck, JoltSumcheckSpec::boolean(8, 3)); + assert!(claims + .input + .required_openings + .contains(&unsigned_inc_opening())); + assert!(claims + .input + .required_openings + .contains(&unsigned_inc_msb_opening())); + assert_eq!( + claims.output.required_openings, + (0..8).map(unsigned_inc_chunk_opening).collect::>() + ); + assert_eq!( + claims.required_challenges(), + vec![JoltChallengeId::from( + UnsignedIncChunkReconstructionChallenge::Gamma, + )] + ); + assert_eq!( + claims.required_publics(), + vec![ + JoltPublicId::from(UnsignedIncChunkReconstructionPublic::EqBooleanityAddress), + JoltPublicId::from(UnsignedIncChunkReconstructionPublic::IdentityAtAddress), + ] + ); +} + +#[test] +fn unsigned_inc_chunking_rejects_invalid_sizes() { + assert_eq!(unsigned_inc_lower_chunk_count(0), None); + assert_eq!(unsigned_inc_lower_chunk_count(3), None); + assert_eq!(unsigned_inc_lower_chunk_count(64), None); + assert!(unsigned_inc_chunk_reconstruction_claim::(3).is_none()); + assert!(unsigned_inc_validity_requirements(3).is_none()); + assert!(unsigned_inc_lower_value_lattice_view_formula::(3).is_none()); + assert!(unsigned_inc_chunk_reconstruction_claim::(64).is_none()); + assert!(unsigned_inc_validity_requirements(64).is_none()); + assert!(unsigned_inc_lower_value_lattice_view_formula::(64).is_none()); +} + +#[test] +fn unsigned_inc_decode_formulas_use_configured_chunks_and_msb() { + assert_eq!( + unsigned_inc_msb_lattice_view_formula::(), + PackingViewFormula::direct(physical(JoltPackingFamilyId::UnsignedIncMsb), 0, 1) + ); + + let lower = unsigned_inc_lower_value_lattice_view_formula::(4) + .expect("4-bit chunking should be valid"); + let terms = linear_decoded_terms(&lower); + assert_eq!(terms.len(), 16 * 16); + assert_eq!( + find_term( + terms, + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 0 }), + 0, + 7 + ) + .coefficient, + Fr::from_u64(7) + ); + assert_eq!( + find_term( + terms, + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 1 }), + 0, + 3 + ) + .coefficient, + Fr::from_u64(16 * 3) + ); + assert_eq!( + find_term( + terms, + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 15 }), + 0, + 2 + ) + .coefficient, + Fr::from_u64(1u64 << 60) * Fr::from_u64(2) + ); +} + +#[test] +fn unsigned_inc_validity_requirements_cover_chunks_and_msb() { + let requirements = + unsigned_inc_validity_requirements(4).expect("4-bit chunking should be valid"); + + assert_eq!(requirements.len(), 17); + assert_eq!( + requirements[0], + PackingValidityRequirement::exact_one_hot( + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 0 }), + 1, + 16, + ) + ); + assert_eq!( + requirements[15], + PackingValidityRequirement::exact_one_hot( + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 15 }), + 1, + 16, + ) + ); + assert_eq!( + requirements[16], + PackingValidityRequirement::boolean_indicator( + physical(JoltPackingFamilyId::UnsignedIncMsb), + 1, + 2, + 1, + ) + ); +} + +#[test] +fn lattice_packed_witness_layout_derives_ra_and_increment_families() { + let ra_layout = JoltRaPolynomialLayout::new(2, 1, 1).unwrap(); + let layout = derive_jolt_lattice_packed_witness_layout(JoltLatticePackingInputs { + log_t: 5, + log_k_chunk: 4, + ra_layout, + field_rd_inc: None, + untrusted_advice: None, + }) + .unwrap(); + + let trace = PackingFactDomain::TraceRows { log_t: 5 }; + let fixed_16 = PackingAlphabet::Fixed { size: 16 }; + assert_eq!( + layout + .family(&physical(JoltPackingFamilyId::InstructionRa { index: 1 })) + .unwrap() + .domain, + trace + ); + assert_eq!( + layout + .family(&physical(JoltPackingFamilyId::InstructionRa { index: 1 })) + .unwrap() + .alphabet, + fixed_16 + ); + assert!(layout + .family(&physical(JoltPackingFamilyId::BytecodeRa { index: 0 })) + .is_some()); + assert!(layout + .family(&physical(JoltPackingFamilyId::RamRa { index: 0 })) + .is_some()); + assert!(layout + .family(&physical(JoltPackingFamilyId::UnsignedIncChunk { + index: 15 + })) + .is_some()); + assert!(layout + .family(&physical(JoltPackingFamilyId::UnsignedIncMsb)) + .is_some()); +} + +#[test] +fn lattice_packed_witness_layout_rejects_invalid_chunking() { + let ra_layout = JoltRaPolynomialLayout::new(1, 1, 1).unwrap(); + let err = derive_jolt_lattice_packed_witness_layout(JoltLatticePackingInputs { + log_t: 5, + log_k_chunk: 3, + ra_layout, + field_rd_inc: None, + untrusted_advice: None, + }) + .unwrap_err(); + + assert_eq!( + err, + JoltLatticeLayoutError::InvalidUnsignedIncChunking { log_k_chunk: 3 } + ); +} + +#[test] +fn lattice_packed_witness_layout_includes_field_and_advice_families() { + let ra_layout = JoltRaPolynomialLayout::new(1, 1, 1).unwrap(); + let advice_layout = advice_layout(5, 4, 64); + let layout = derive_jolt_lattice_packed_witness_layout(JoltLatticePackingInputs { + log_t: 5, + log_k_chunk: 4, + ra_layout, + field_rd_inc: Some(FieldRdIncPacking { + byte_width: 2, + canonical_modulus: Some(257), + }), + untrusted_advice: Some(&advice_layout), + }) + .unwrap(); + + assert!(layout_has_field_rd_inc(&layout)); + assert!(layout_has_advice(&layout, PackingAdviceKind::Untrusted)); + assert_eq!( + layout + .family(&physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Untrusted, + index: 0, + })) + .unwrap() + .domain, + PackingFactDomain::AdviceBytes { + kind: PackingAdviceKind::Untrusted, + log_bytes: 6, + } + ); + + let requirements = + derive_jolt_lattice_packed_validity_requirements(JoltLatticeValidityInputs { + log_k_chunk: 4, + field_rd_inc: Some(FieldRdIncPacking { + byte_width: 2, + canonical_modulus: Some(257), + }), + untrusted_advice: Some(&advice_layout), + }) + .unwrap(); + assert!( + requirements.contains(&PackingValidityRequirement::field_element_canonical_bytes( + physical(JoltPackingFamilyId::FieldRdIncByte { index: 0 }), + 2, + 257, + )) + ); + assert!(requirements.contains(&advice_bytes_validity_requirement( + JoltAdviceKind::Untrusted, + ))); +} + +#[test] +fn layout_derived_validity_marks_bytecode_store_rd_disjointness() { + let layout = PackingWitnessLayout::new([ + PackingFamilySpec::direct( + physical(JoltPackingFamilyId::BytecodeCircuitFlag { + chunk: 0, + flag: CircuitFlags::Store as usize, + }), + PackingFactDomain::BytecodeRows { log_bytecode: 4 }, + 1, + PackingAlphabet::Bit, + ), + PackingFamilySpec::direct( + physical(JoltPackingFamilyId::BytecodeRegisterSelector { + chunk: 0, + selector: 2, + }), + PackingFactDomain::BytecodeRows { log_bytecode: 4 }, + 1, + PackingAlphabet::Fixed { + size: 1 << REGISTER_ADDRESS_BITS, + }, + ), + ]) + .unwrap(); + + let requirements = lattice_validity_requirements_for_packed_witness_layout(&layout) + .expect("layout validity requirements should derive"); + assert!( + requirements.contains(&PackingValidityRequirement::bytecode_store_rd_disjoint( + physical(JoltPackingFamilyId::BytecodeCircuitFlag { + chunk: 0, + flag: CircuitFlags::Store as usize, + }) + )) + ); +} + +#[test] +fn precommitted_family_classification_keeps_untrusted_advice_packable() { + assert!(packed_family_is_precommitted(&physical( + JoltPackingFamilyId::BytecodeChunk { index: 0 } + ))); + assert!(packed_family_is_precommitted(&physical( + JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Trusted, + index: 0, + } + ))); + assert!(!packed_family_is_precommitted(&physical( + JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Untrusted, + index: 0, + } + ))); +} + +#[test] +fn base_increment_validity_requirements_do_not_use_field_inline_families() { + let requirements = + unsigned_inc_validity_requirements(8).expect("8-bit chunking should be valid"); + + assert!(requirements.iter().all(|requirement| { + !matches!( + JoltPackingFamilyId::from_physical_id(&requirement.family), + Some(JoltPackingFamilyId::FieldRdIncByte { .. } | JoltPackingFamilyId::FieldRdIncSign) + ) + })); +} + +#[test] +fn packed_validity_digest_is_order_stable_and_kind_sensitive() { + let exact = PackingValidityRequirement::exact_one_hot( + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 0 }), + 1, + 256, + ); + let msb = PackingValidityRequirement::boolean_indicator( + physical(JoltPackingFamilyId::UnsignedIncMsb), + 1, + 2, + 1, + ); + let optional = PackingValidityRequirement::optional_one_hot( + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 0 }), + 1, + 256, + ); + + assert_eq!( + packing_validity_digest(&[exact.clone(), msb.clone()]), + packing_validity_digest(&[msb, exact.clone()]) + ); + assert_ne!( + packing_validity_digest(&[exact]), + packing_validity_digest(&[optional]) + ); +} + +#[test] +fn bytecode_validity_requirements_cover_committed_program_facts() { + let chunk = 2; + let field_byte_width = 16; + let requirements = bytecode_validity_requirements(chunk, field_byte_width); + + assert_eq!( + requirements.len(), + 3 + NUM_CIRCUIT_FLAGS + NUM_INSTRUCTION_FLAGS + 5 + ); + assert!( + requirements.contains(&PackingValidityRequirement::optional_one_hot( + physical(JoltPackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }), + 1, + 1 << REGISTER_ADDRESS_BITS, + )) + ); + assert!( + requirements.contains(&PackingValidityRequirement::boolean_indicator( + physical(JoltPackingFamilyId::BytecodeCircuitFlag { chunk, flag: 0 }), + 1, + 2, + 1, + )) + ); + assert!( + requirements.contains(&PackingValidityRequirement::boolean_indicator( + physical(JoltPackingFamilyId::BytecodeInstructionFlag { + chunk, + flag: NUM_INSTRUCTION_FLAGS - 1, + }), + 1, + 2, + 1, + )) + ); + assert!( + requirements.contains(&PackingValidityRequirement::optional_one_hot( + physical(JoltPackingFamilyId::BytecodeLookupSelector { chunk }), + 1, + LookupTableKind::::COUNT.next_power_of_two(), + )) + ); + assert!( + requirements.contains(&PackingValidityRequirement::boolean_indicator( + physical(JoltPackingFamilyId::BytecodeRafFlag { chunk }), + 1, + 2, + 1, + )) + ); + assert!( + requirements.contains(&PackingValidityRequirement::exact_one_hot( + physical(JoltPackingFamilyId::BytecodeUnexpandedPcBytes { chunk }), + 8, + 256, + )) + ); + assert!( + requirements.contains(&PackingValidityRequirement::exact_one_hot( + physical(JoltPackingFamilyId::BytecodeImmBytes { chunk }), + field_byte_width, + 256, + )) + ); + assert!( + requirements.contains(&PackingValidityRequirement::bytecode_store_rd_disjoint( + physical(JoltPackingFamilyId::BytecodeCircuitFlag { + chunk, + flag: CircuitFlags::Store as usize, + }) + )) + ); +} + +#[test] +fn bytecode_imm_canonical_bytes_requirement_anchors_bytecode_immediates() { + assert_eq!( + bytecode_imm_canonical_bytes_requirement(2, 16, 97), + PackingValidityRequirement::field_element_canonical_bytes( + physical(JoltPackingFamilyId::BytecodeImmBytes { chunk: 2 }), + 16, + 97, + ) + ); +} + +#[test] +fn advice_and_program_image_validity_requirements_are_byte_facts() { + assert_eq!( + advice_bytes_validity_requirement(JoltAdviceKind::Trusted), + PackingValidityRequirement::exact_one_hot( + physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Trusted, + index: 0, + }), + 1, + 256, + ) + ); + assert_eq!( + program_image_validity_requirement(), + PackingValidityRequirement::exact_one_hot( + physical(JoltPackingFamilyId::ProgramImageInit), + 8, + 256, + ) + ); +} + +#[test] +fn byte_decode_terms_are_little_endian_symbol_weights() { + let terms = + byte_decode_terms::(physical(JoltPackingFamilyId::BytecodeChunk { index: 0 }), 3); + + assert_eq!(terms.len(), 256); + assert_eq!(terms[7].coefficient, Fr::from_u64(7)); + assert_eq!( + terms[7].family, + physical(JoltPackingFamilyId::BytecodeChunk { index: 0 }) + ); + assert_eq!(terms[7].limb, 3); + assert_eq!(terms[7].symbol, 7); +} + +#[test] +fn committed_bytecode_lattice_family_ids_name_lane_classes() { + assert_ne!( + physical(JoltPackingFamilyId::BytecodeRegisterSelector { + chunk: 0, + selector: 0, + }), + physical(JoltPackingFamilyId::BytecodeRegisterSelector { + chunk: 0, + selector: 1, + }) + ); + assert_ne!( + physical(JoltPackingFamilyId::BytecodeUnexpandedPcBytes { chunk: 0 }), + physical(JoltPackingFamilyId::BytecodeImmBytes { chunk: 0 }) + ); + assert_ne!( + physical(JoltPackingFamilyId::BytecodeCircuitFlag { chunk: 0, flag: 0 }), + physical(JoltPackingFamilyId::BytecodeInstructionFlag { chunk: 0, flag: 0 }) + ); + let circuit_flag = JoltPackingFamilyId::BytecodeCircuitFlag { chunk: 0, flag: 1 }; + let other_circuit_flag = JoltPackingFamilyId::BytecodeCircuitFlag { chunk: 1, flag: 0 }; + assert_ne!(physical(circuit_flag), physical(other_circuit_flag)); + assert_eq!( + JoltPackingFamilyId::from_physical_id(&physical(circuit_flag)), + Some(circuit_flag) + ); + assert_eq!( + JoltPackingFamilyId::from_physical_id(&physical(other_circuit_flag)), + Some(other_circuit_flag) + ); +} + +#[test] +fn layout_derived_validity_rejects_non_jolt_family_ids() { + let layout = PackingWitnessLayout::new([PackingFamilySpec::direct( + PackingFamilyId::new(0x7465_7374, 1, 0), + PackingFactDomain::TraceRows { log_t: 0 }, + 1, + PackingAlphabet::Bit, + )]) + .unwrap(); + + assert!(matches!( + lattice_validity_requirements_for_packed_witness_layout(&layout), + Err(JoltLatticeLayoutError::UnsupportedPackingFamily { .. }) + )); +} + +#[test] +fn bytecode_chunk_lattice_view_formula_uses_cycle_major_lane_weights() { + let lane_vars = bytecode_reduction::committed_lane_vars(); + let log_bytecode = 2; + let lane_point = (1..=lane_vars as u64).map(Fr::from_u64).collect::>(); + let mut opening_point = lane_point.clone(); + opening_point.extend([Fr::from_u64(101), Fr::from_u64(103)]); + let lane_weights = EqPolynomial::::evals(&lane_point, None); + let lane_layout = bytecode_reduction::BYTECODE_LANE_LAYOUT; + + let formula = bytecode_chunk_lattice_view_formula( + 2, + &opening_point, + TracePolynomialOrder::CycleMajor, + log_bytecode, + 2, + ) + .unwrap(); + let terms = linear_decoded_terms(&formula); + + assert_eq!( + find_term( + terms, + physical(JoltPackingFamilyId::BytecodeRegisterSelector { + chunk: 2, + selector: 2 + }), + 0, + 5 + ) + .coefficient, + lane_weights[lane_layout.rd_start + 5] + ); + assert_eq!( + find_term( + terms, + physical(JoltPackingFamilyId::BytecodeCircuitFlag { chunk: 2, flag: 0 }), + 0, + 1 + ) + .coefficient, + lane_weights[lane_layout.circuit_start] + ); + assert_eq!( + find_term( + terms, + physical(JoltPackingFamilyId::BytecodeLookupSelector { chunk: 2 }), + 0, + 3 + ) + .coefficient, + lane_weights[lane_layout.lookup_start + 3] + ); + assert_eq!( + find_term( + terms, + physical(JoltPackingFamilyId::BytecodeUnexpandedPcBytes { chunk: 2 }), + 1, + 7 + ) + .coefficient, + lane_weights[lane_layout.unexp_pc_idx] * Fr::from_u64(256 * 7) + ); + assert_eq!( + find_term( + terms, + physical(JoltPackingFamilyId::BytecodeImmBytes { chunk: 2 }), + 1, + 9 + ) + .coefficient, + lane_weights[lane_layout.imm_idx] * Fr::from_u64(256 * 9) + ); + assert_eq!( + find_term( + terms, + physical(JoltPackingFamilyId::BytecodeRafFlag { chunk: 2 }), + 0, + 1 + ) + .coefficient, + lane_weights[lane_layout.raf_flag_idx] + ); +} + +#[test] +fn bytecode_chunk_lattice_view_formula_uses_address_major_lane_suffix() { + let lane_vars = bytecode_reduction::committed_lane_vars(); + let log_bytecode = 2; + let lane_point = (11..11 + lane_vars as u64) + .map(Fr::from_u64) + .collect::>(); + let mut opening_point = vec![Fr::from_u64(101), Fr::from_u64(103)]; + opening_point.extend(lane_point.iter().copied()); + let lane_weights = EqPolynomial::::evals(&lane_point, None); + + let formula = bytecode_chunk_lattice_view_formula( + 1, + &opening_point, + TracePolynomialOrder::AddressMajor, + log_bytecode, + 1, + ) + .unwrap(); + let terms = linear_decoded_terms(&formula); + + assert_eq!( + find_term( + terms, + physical(JoltPackingFamilyId::BytecodeRegisterSelector { + chunk: 1, + selector: 0 + }), + 0, + 1 + ) + .coefficient, + lane_weights[bytecode_reduction::BYTECODE_LANE_LAYOUT.rs1_start + 1] + ); +} + +#[test] +fn bytecode_chunk_lattice_view_formula_rejects_bad_point_length() { + let expected = bytecode_reduction::committed_lane_vars() + 3; + + let err = bytecode_chunk_lattice_view_formula::( + 0, + &vec![Fr::from_u64(0); expected - 1], + TracePolynomialOrder::CycleMajor, + 3, + 1, + ) + .unwrap_err(); + + assert_eq!( + err, + JoltFormulaPointError::OpeningPointLengthMismatch { + expected, + got: expected - 1 + } + ); +} + +#[test] +fn symbol_decode_terms_support_non_byte_alphabets() { + let family = physical(JoltPackingFamilyId::RamRa { index: 1 }); + let terms = symbol_decode_terms::(family, 0, 4); + + assert_eq!(terms.len(), 4); + assert_eq!(terms[3].coefficient, Fr::from_u64(3)); + assert_eq!(terms[3].family, family); + assert_eq!(terms[3].limb, 0); + assert_eq!(terms[3].symbol, 3); +} + +#[test] +fn weighted_symbol_terms_use_supplied_coefficients() { + let terms = weighted_symbol_terms( + physical(JoltPackingFamilyId::InstructionRa { index: 0 }), + 2, + [Fr::from_u64(11), Fr::from_u64(13), Fr::from_u64(17)], + ); + + assert_eq!(terms.len(), 3); + assert_eq!(terms[1].coefficient, Fr::from_u64(13)); + assert_eq!( + terms[1].family, + physical(JoltPackingFamilyId::InstructionRa { index: 0 }) + ); + assert_eq!(terms[1].limb, 2); + assert_eq!(terms[1].symbol, 1); +} + +#[test] +fn weighted_byte_decode_terms_scale_symbols_by_limb_weights() { + let terms = weighted_byte_decode_terms( + physical(JoltPackingFamilyId::BytecodeChunk { index: 2 }), + [(3, Fr::from_u64(5)), (8, Fr::from_u64(7))], + ); + + assert_eq!(terms.len(), 512); + assert_eq!(terms[9].coefficient, Fr::from_u64(45)); + assert_eq!(terms[9].limb, 3); + assert_eq!(terms[9].symbol, 9); + assert_eq!(terms[256 + 9].coefficient, Fr::from_u64(63)); + assert_eq!(terms[256 + 9].limb, 8); + assert_eq!( + terms[256 + 9].family, + physical(JoltPackingFamilyId::BytecodeChunk { index: 2 }) + ); +} + +#[test] +fn little_endian_byte_decode_terms_weight_limbs_by_place_value() { + let terms = + little_endian_byte_decode_terms::(physical(JoltPackingFamilyId::ProgramImageInit), 2); + + assert_eq!(terms.len(), 512); + assert_eq!(terms[7].coefficient, Fr::from_u64(7)); + assert_eq!(terms[7].limb, 0); + assert_eq!(terms[7].symbol, 7); + assert_eq!(terms[256 + 7].coefficient, Fr::from_u64(256 * 7)); + assert_eq!(terms[256 + 7].limb, 1); + assert_eq!(terms[256 + 7].symbol, 7); + assert_eq!( + terms[256 + 7].family, + physical(JoltPackingFamilyId::ProgramImageInit) + ); +} + +fn advice_layout( + log_t: usize, + log_k_chunk: usize, + max_advice_size_bytes: usize, +) -> AdviceClaimReductionLayout { + let advice_vars = + CommitmentMatrixShape::advice_from_max_bytes(max_advice_size_bytes).total_vars(); + let scheduling_reference = PrecommittedClaimReduction::scheduling_reference( + log_t + log_k_chunk, + &[advice_vars], + log_k_chunk, + ); + AdviceClaimReductionLayout::balanced( + TracePolynomialOrder::CycleMajor, + log_t, + scheduling_reference, + max_advice_size_bytes, + ) + .unwrap() +} + +fn linear_decoded_terms(formula: &PackingViewFormula) -> &[PackingViewTerm] { + match formula { + PackingViewFormula::LinearDecoded { terms, .. } => terms, + _ => panic!("expected linear decoded formula"), + } +} + +fn find_term( + terms: &[PackingViewTerm], + family: PackingFamilyId, + limb: usize, + symbol: usize, +) -> &PackingViewTerm { + terms + .iter() + .find(|term| term.family == family && term.limb == limb && term.symbol == symbol) + .unwrap() +} diff --git a/crates/jolt-claims/src/protocols/jolt/formulas/lattice/validity.rs b/crates/jolt-claims/src/protocols/jolt/formulas/lattice/validity.rs new file mode 100644 index 0000000000..8b489d1e84 --- /dev/null +++ b/crates/jolt-claims/src/protocols/jolt/formulas/lattice/validity.rs @@ -0,0 +1,116 @@ +use jolt_lookup_tables::{LookupTableKind, XLEN}; +use jolt_openings::{PackingFamilyId, PackingValidityRequirement}; +use jolt_riscv::{CircuitFlags, NUM_CIRCUIT_FLAGS, NUM_INSTRUCTION_FLAGS}; + +use crate::protocols::jolt::JoltAdviceKind; + +use super::super::dimensions::REGISTER_ADDRESS_BITS; +use super::families::JoltPackingFamilyId; +use super::openings::unsigned_inc_chunking; + +pub fn unsigned_inc_validity_requirements( + log_k_chunk: usize, +) -> Option> { + let chunking = unsigned_inc_chunking(log_k_chunk)?; + let mut requirements = (0..chunking.chunk_count) + .map(|index| { + PackingValidityRequirement::exact_one_hot( + JoltPackingFamilyId::UnsignedIncChunk { index }.into(), + 1, + chunking.alphabet_size, + ) + }) + .collect::>(); + requirements.push(PackingValidityRequirement::boolean_indicator( + JoltPackingFamilyId::UnsignedIncMsb.into(), + 1, + 2, + 1, + )); + Some(requirements) +} + +pub fn advice_bytes_validity_requirement(kind: JoltAdviceKind) -> PackingValidityRequirement { + byte_validity_requirement( + JoltPackingFamilyId::AdviceBytes { kind, index: 0 }.into(), + 1, + ) +} + +pub fn program_image_validity_requirement() -> PackingValidityRequirement { + byte_validity_requirement(JoltPackingFamilyId::ProgramImageInit.into(), 8) +} + +pub fn bytecode_validity_requirements( + chunk: usize, + field_byte_width: usize, +) -> Vec { + let register_count = 1usize << REGISTER_ADDRESS_BITS; + let mut requirements = Vec::new(); + for selector in 0..3 { + requirements.push(PackingValidityRequirement::optional_one_hot( + JoltPackingFamilyId::BytecodeRegisterSelector { chunk, selector }.into(), + 1, + register_count, + )); + } + for flag in 0..NUM_CIRCUIT_FLAGS { + requirements.push(PackingValidityRequirement::boolean_indicator( + JoltPackingFamilyId::BytecodeCircuitFlag { chunk, flag }.into(), + 1, + 2, + 1, + )); + } + for flag in 0..NUM_INSTRUCTION_FLAGS { + requirements.push(PackingValidityRequirement::boolean_indicator( + JoltPackingFamilyId::BytecodeInstructionFlag { chunk, flag }.into(), + 1, + 2, + 1, + )); + } + requirements.push(PackingValidityRequirement::optional_one_hot( + JoltPackingFamilyId::BytecodeLookupSelector { chunk }.into(), + 1, + LookupTableKind::::COUNT.next_power_of_two(), + )); + requirements.push(PackingValidityRequirement::boolean_indicator( + JoltPackingFamilyId::BytecodeRafFlag { chunk }.into(), + 1, + 2, + 1, + )); + requirements.push(byte_validity_requirement( + JoltPackingFamilyId::BytecodeUnexpandedPcBytes { chunk }.into(), + 8, + )); + requirements.push(byte_validity_requirement( + JoltPackingFamilyId::BytecodeImmBytes { chunk }.into(), + field_byte_width, + )); + requirements.push(PackingValidityRequirement::bytecode_store_rd_disjoint( + JoltPackingFamilyId::BytecodeCircuitFlag { + chunk, + flag: CircuitFlags::Store as usize, + } + .into(), + )); + requirements +} + +pub fn bytecode_imm_canonical_bytes_requirement( + chunk: usize, + byte_width: usize, + modulus: u128, +) -> PackingValidityRequirement { + PackingValidityRequirement::field_element_canonical_bytes( + JoltPackingFamilyId::BytecodeImmBytes { chunk }.into(), + byte_width, + modulus, + ) +} + +fn byte_validity_requirement(family: PackingFamilyId, limbs: usize) -> PackingValidityRequirement { + PackingValidityRequirement::exact_one_hot(family, limbs, 256) +} diff --git a/crates/jolt-claims/src/protocols/jolt/formulas/lattice/views.rs b/crates/jolt-claims/src/protocols/jolt/formulas/lattice/views.rs new file mode 100644 index 0000000000..2078b403b1 --- /dev/null +++ b/crates/jolt-claims/src/protocols/jolt/formulas/lattice/views.rs @@ -0,0 +1,212 @@ +use jolt_field::Field; +use jolt_lookup_tables::{LookupTableKind, XLEN}; +use jolt_openings::{PackingFamilyId, PackingViewFormula, PackingViewTerm}; +use jolt_poly::EqPolynomial; +use jolt_riscv::{CircuitFlags, NUM_CIRCUIT_FLAGS, NUM_INSTRUCTION_FLAGS}; + +use super::super::claim_reductions::bytecode as bytecode_reduction; +use super::super::dimensions::{ + JoltFormulaPointError, TracePolynomialOrder, REGISTER_ADDRESS_BITS, +}; +use super::families::JoltPackingFamilyId; +use super::openings::unsigned_inc_chunking; + +pub fn bytecode_store_flag_lattice_view_formula(chunk: usize) -> PackingViewFormula { + PackingViewFormula::direct( + JoltPackingFamilyId::BytecodeCircuitFlag { + chunk, + flag: CircuitFlags::Store as usize, + } + .into(), + 0, + 1, + ) +} + +pub fn bytecode_rd_present_lattice_view_formula(chunk: usize) -> PackingViewFormula { + PackingViewFormula::linear_decoded(weighted_symbol_terms( + JoltPackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }.into(), + 0, + [F::one(); 1 << REGISTER_ADDRESS_BITS], + )) +} + +pub fn unsigned_inc_lower_value_lattice_view_formula( + log_k_chunk: usize, +) -> Option> { + Some(PackingViewFormula::linear_decoded( + unsigned_inc_lower_value_terms(log_k_chunk)?, + )) +} + +pub fn unsigned_inc_lower_value_terms( + log_k_chunk: usize, +) -> Option>> { + let chunking = unsigned_inc_chunking(log_k_chunk)?; + let mut terms = Vec::with_capacity(chunking.chunk_count * chunking.alphabet_size); + let mut place = F::one(); + for index in 0..chunking.chunk_count { + terms.extend(weighted_symbol_terms( + JoltPackingFamilyId::UnsignedIncChunk { index }.into(), + 0, + (0..chunking.alphabet_size).map(|symbol| place * F::from_u64(symbol as u64)), + )); + place *= F::from_u64(chunking.radix); + } + Some(terms) +} + +pub fn unsigned_inc_msb_lattice_view_formula() -> PackingViewFormula { + PackingViewFormula::direct(JoltPackingFamilyId::UnsignedIncMsb.into(), 0, 1) +} + +pub fn byte_decode_terms( + family: PackingFamilyId, + limb: usize, +) -> Vec> { + weighted_byte_decode_terms(family, [(limb, F::one())]) +} + +pub fn symbol_decode_terms( + family: PackingFamilyId, + limb: usize, + alphabet_size: usize, +) -> Vec> { + weighted_symbol_terms( + family, + limb, + (0..alphabet_size).map(|symbol| F::from_u64(symbol as u64)), + ) +} + +pub fn weighted_symbol_terms( + family: PackingFamilyId, + limb: usize, + weights: impl IntoIterator, +) -> Vec> { + weights + .into_iter() + .enumerate() + .map(|(symbol, coefficient)| PackingViewTerm::new(coefficient, family, limb, symbol)) + .collect() +} + +pub fn weighted_byte_decode_terms( + family: PackingFamilyId, + limb_weights: impl IntoIterator, +) -> Vec> { + limb_weights + .into_iter() + .flat_map(|(limb, limb_weight)| { + (0..256).map(move |symbol| { + PackingViewTerm::new( + limb_weight * F::from_u64(symbol as u64), + family, + limb, + symbol, + ) + }) + }) + .collect() +} + +pub fn little_endian_byte_decode_terms( + family: PackingFamilyId, + limb_count: usize, +) -> Vec> { + let mut limb_weights = Vec::with_capacity(limb_count); + let mut place = F::one(); + for limb in 0..limb_count { + limb_weights.push((limb, place)); + place *= F::from_u64(256); + } + weighted_byte_decode_terms(family, limb_weights) +} + +pub fn bytecode_chunk_lattice_view_formula( + chunk: usize, + opening_point: &[F], + trace_order: TracePolynomialOrder, + log_bytecode: usize, + field_byte_width: usize, +) -> Result, JoltFormulaPointError> { + let lane_vars = bytecode_reduction::committed_lane_vars(); + let expected = lane_vars + log_bytecode; + if opening_point.len() != expected { + return Err(JoltFormulaPointError::OpeningPointLengthMismatch { + expected, + got: opening_point.len(), + }); + } + let lane_point = match trace_order { + TracePolynomialOrder::CycleMajor => &opening_point[..lane_vars], + TracePolynomialOrder::AddressMajor => &opening_point[log_bytecode..], + }; + let lane_weights = EqPolynomial::::evals(lane_point, None); + let lane_layout = bytecode_reduction::BYTECODE_LANE_LAYOUT; + let register_count = 1usize << REGISTER_ADDRESS_BITS; + let mut terms = Vec::new(); + + for selector in 0..3 { + let start = match selector { + 0 => lane_layout.rs1_start, + 1 => lane_layout.rs2_start, + _ => lane_layout.rd_start, + }; + terms.extend(weighted_symbol_terms( + JoltPackingFamilyId::BytecodeRegisterSelector { chunk, selector }.into(), + 0, + lane_weights[start..start + register_count].iter().copied(), + )); + } + terms.extend(weighted_byte_decode_terms( + JoltPackingFamilyId::BytecodeUnexpandedPcBytes { chunk }.into(), + byte_limb_weights(lane_weights[lane_layout.unexp_pc_idx], 8), + )); + terms.extend(weighted_byte_decode_terms( + JoltPackingFamilyId::BytecodeImmBytes { chunk }.into(), + byte_limb_weights(lane_weights[lane_layout.imm_idx], field_byte_width), + )); + for flag in 0..NUM_CIRCUIT_FLAGS { + terms.push(PackingViewTerm::new( + lane_weights[lane_layout.circuit_start + flag], + JoltPackingFamilyId::BytecodeCircuitFlag { chunk, flag }.into(), + 0, + 1, + )); + } + for flag in 0..NUM_INSTRUCTION_FLAGS { + terms.push(PackingViewTerm::new( + lane_weights[lane_layout.instr_start + flag], + JoltPackingFamilyId::BytecodeInstructionFlag { chunk, flag }.into(), + 0, + 1, + )); + } + terms.extend(weighted_symbol_terms( + JoltPackingFamilyId::BytecodeLookupSelector { chunk }.into(), + 0, + lane_weights + [lane_layout.lookup_start..lane_layout.lookup_start + LookupTableKind::::COUNT] + .iter() + .copied(), + )); + terms.push(PackingViewTerm::new( + lane_weights[lane_layout.raf_flag_idx], + JoltPackingFamilyId::BytecodeRafFlag { chunk }.into(), + 0, + 1, + )); + + Ok(PackingViewFormula::linear_decoded(terms)) +} + +fn byte_limb_weights(lane_weight: F, limb_count: usize) -> Vec<(usize, F)> { + let mut weights = Vec::with_capacity(limb_count); + let mut place = F::one(); + for limb in 0..limb_count { + weights.push((limb, lane_weight * place)); + place *= F::from_u64(256); + } + weights +} diff --git a/crates/jolt-claims/src/protocols/jolt/mod.rs b/crates/jolt-claims/src/protocols/jolt/mod.rs index ed89368bab..9bb2fe0a51 100644 --- a/crates/jolt-claims/src/protocols/jolt/mod.rs +++ b/crates/jolt-claims/src/protocols/jolt/mod.rs @@ -3,6 +3,44 @@ pub mod formulas; mod ids; mod relation; +pub use formulas::lattice::JoltPackingFamilyId; +pub use formulas::lattice::{ + advice_bytes_validity_requirement, bytecode_imm_canonical_bytes_requirement, + bytecode_validity_requirements, packing_validity_digest, program_image_validity_requirement, + unsigned_inc_validity_requirements, +}; +pub use formulas::lattice::{ + byte_decode_terms, bytecode_chunk_lattice_view_formula, + bytecode_rd_present_lattice_view_formula, bytecode_store_flag_lattice_view_formula, + little_endian_byte_decode_terms, symbol_decode_terms, + unsigned_inc_lower_value_lattice_view_formula, unsigned_inc_lower_value_terms, + unsigned_inc_msb_lattice_view_formula, weighted_byte_decode_terms, weighted_symbol_terms, +}; +pub use formulas::lattice::{ + derive_jolt_lattice_packed_validity_requirements, derive_jolt_lattice_packed_witness_layout, + jolt_advice_kind, lattice_validity_requirements_for_packed_witness_layout, layout_has_advice, + layout_has_field_rd_inc, packed_alphabet_with_size, packed_family_is_precommitted, + packing_advice_kind, +}; +pub use formulas::lattice::{ + final_opening_lattice_requirement, inc_virtualization_inc_opening, + inc_virtualization_input_openings, inc_virtualization_output_openings, + inc_virtualization_relation, inc_virtualization_store_opening, unsigned_inc_chunk_opening, + unsigned_inc_chunk_reconstruction_relation, unsigned_inc_claim_reduction_relation, + unsigned_inc_input_opening, unsigned_inc_lower_chunk_count, unsigned_inc_msb_opening, + unsigned_inc_opening, +}; +pub use formulas::lattice::{ + inc_virtualization_claim, unsigned_inc_chunk_reconstruction_claim, + unsigned_inc_claim_reduction_claim, unsigned_inc_msb_booleanity_claim, +}; +pub use formulas::lattice::{ + FieldRdIncPacking, JoltLatticeLayoutError, JoltLatticePackingInputs, JoltLatticeValidityInputs, +}; +pub use formulas::lattice::{ + LatticeFinalOpeningRequirement, PackingValidityDigest, PackingValidityKind, + PackingValidityRequirement, PackingViewFormula, PackingViewTerm, UNSIGNED_INC_BITS, +}; pub use formulas::{ claim_reductions::advice::AdviceClaimReductionLayout, claim_reductions::bytecode::BytecodeClaimReductionLayout, @@ -17,25 +55,6 @@ pub use formulas::{ TraceDimensions, TracePolynomialOrder, }, error::{JoltFormulaDimensionsError, JoltFormulaPointError}, - lattice::{ - advice_bytes_validity_requirement, byte_decode_terms, bytecode_chunk_lattice_view_formula, - bytecode_imm_canonical_bytes_requirement, bytecode_rd_present_lattice_view_formula, - bytecode_store_flag_lattice_view_formula, bytecode_validity_requirements, - final_opening_lattice_requirement, inc_virtualization_claim, - inc_virtualization_inc_opening, inc_virtualization_input_openings, - inc_virtualization_output_openings, inc_virtualization_relation, - inc_virtualization_store_opening, little_endian_byte_decode_terms, packing_validity_digest, - program_image_validity_requirement, symbol_decode_terms, unsigned_inc_chunk_opening, - unsigned_inc_chunk_reconstruction_claim, unsigned_inc_chunk_reconstruction_relation, - unsigned_inc_claim_reduction_claim, unsigned_inc_claim_reduction_relation, - unsigned_inc_input_opening, unsigned_inc_lower_chunk_count, - unsigned_inc_lower_value_lattice_view_formula, unsigned_inc_lower_value_terms, - unsigned_inc_msb_booleanity_claim, unsigned_inc_msb_lattice_view_formula, - unsigned_inc_msb_opening, unsigned_inc_opening, unsigned_inc_validity_requirements, - weighted_byte_decode_terms, weighted_symbol_terms, LatticeFinalOpeningRequirement, - PackingFamilyId, PackingValidityDigest, PackingValidityKind, PackingValidityRequirement, - PackingViewFormula, PackingViewTerm, UNSIGNED_INC_BITS, - }, }; pub use ids::{ AdviceClaimReductionPublic, BooleanityChallenge, BooleanityPublic, diff --git a/crates/jolt-openings/src/packing_layout.rs b/crates/jolt-openings/src/packing_layout.rs index 3193121280..6f8fde6e1d 100644 --- a/crates/jolt-openings/src/packing_layout.rs +++ b/crates/jolt-openings/src/packing_layout.rs @@ -25,7 +25,7 @@ impl PackingWitnessLayout { if specs.is_empty() { return Err(PackingLayoutError::EmptyLayout); } - specs.sort_by(|left, right| left.id.cmp(&right.id)); + specs.sort_by_key(|spec| spec.id); let mut families = Vec::with_capacity(specs.len()); let mut offset = 0usize; @@ -34,7 +34,7 @@ impl PackingWitnessLayout { if previous_id.as_ref() == Some(&spec.id) { return Err(PackingLayoutError::DuplicateFamily { id: spec.id }); } - previous_id = Some(spec.id.clone()); + previous_id = Some(spec.id); if spec.limbs == 0 { return Err(PackingLayoutError::ZeroLimbs { id: spec.id }); @@ -47,15 +47,11 @@ impl PackingWitnessLayout { let row_cells = rows .checked_mul(spec.limbs) .and_then(|value| value.checked_mul(alphabet_size)) - .ok_or_else(|| PackingLayoutError::CellCountOverflow { - id: spec.id.clone(), - })?; + .ok_or(PackingLayoutError::CellCountOverflow { id: spec.id })?; let cell_count = row_cells; - let next_offset = offset.checked_add(cell_count).ok_or_else(|| { - PackingLayoutError::CellCountOverflow { - id: spec.id.clone(), - } - })?; + let next_offset = offset + .checked_add(cell_count) + .ok_or(PackingLayoutError::CellCountOverflow { id: spec.id })?; families.push(PackingLayoutFamily { id: spec.id, @@ -89,16 +85,14 @@ impl PackingWitnessLayout { } pub fn rank(&self, address: &PackingCellAddress) -> Result { - let family = - self.family(&address.family) - .ok_or_else(|| PackingLayoutError::MissingFamily { - id: address.family.clone(), - })?; + let family = self + .family(&address.family) + .ok_or(PackingLayoutError::MissingFamily { id: address.family })?; let rows = family.domain.rows()?; let alphabet_size = family.alphabet.size(); if address.row >= rows || address.limb >= family.limbs || address.symbol >= alphabet_size { return Err(PackingLayoutError::AddressOutOfRange { - family: address.family.clone(), + family: address.family, row: address.row, limb: address.limb, symbol: address.symbol, @@ -111,15 +105,11 @@ impl PackingWitnessLayout { .and_then(|value| value.checked_add(address.limb)) .and_then(|value| value.checked_mul(alphabet_size)) .and_then(|value| value.checked_add(address.symbol)) - .ok_or_else(|| PackingLayoutError::CellCountOverflow { - id: address.family.clone(), - })?; + .ok_or(PackingLayoutError::CellCountOverflow { id: address.family })?; family .offset .checked_add(local) - .ok_or_else(|| PackingLayoutError::CellCountOverflow { - id: address.family.clone(), - }) + .ok_or(PackingLayoutError::CellCountOverflow { id: address.family }) } pub fn unrank(&self, rank: usize) -> Option { @@ -142,7 +132,7 @@ impl PackingWitnessLayout { let limb = row_limb % family.limbs; let row = row_limb / family.limbs; Some(PackingCellAddress { - family: family.id.clone(), + family: family.id, row, limb, symbol, @@ -232,7 +222,7 @@ impl PackingWitnessLayout { ) -> Result<(), PackingLayoutError> { for id in families { if self.family(id).is_none() { - return Err(PackingLayoutError::MissingViewFamily { id: id.clone() }); + return Err(PackingLayoutError::MissingViewFamily { id: *id }); } } Ok(()) @@ -278,7 +268,7 @@ impl PackingLayout for PackingWitnessLayout { OpeningsError::InvalidBatch("packing term references an unknown family".to_string()) })?; self.rank(&PackingCellAddress { - family: family.id.clone(), + family: family.id, row: address.row, limb: address.limb, symbol: address.symbol, @@ -338,102 +328,25 @@ impl PackingFamilySpec { } } -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub enum PackingFamilyId { - InstructionRa { - index: usize, - }, - BytecodeRa { - index: usize, - }, - RamRa { - index: usize, - }, - UnsignedIncChunk { - index: usize, - }, - UnsignedIncMsb, - FieldRdIncByte { - index: usize, - }, - FieldRdIncSign, - AdviceBytes { - kind: PackingAdviceKind, - index: usize, - }, - BytecodeChunk { - index: usize, - }, - BytecodeRegisterSelector { - chunk: usize, - selector: usize, - }, - BytecodeCircuitFlag { - chunk: usize, - flag: usize, - }, - BytecodeInstructionFlag { - chunk: usize, - flag: usize, - }, - BytecodeLookupSelector { - chunk: usize, - }, - BytecodeRafFlag { - chunk: usize, - }, - BytecodeUnexpandedPcBytes { - chunk: usize, - }, - BytecodeImmBytes { - chunk: usize, - }, - ProgramImageInit, - Custom { - namespace: u32, - index: usize, - }, +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct PackingFamilyId { + pub namespace: u64, + pub id: u64, + pub index: u64, } -const JOLT_PACKING_FAMILY_NAMESPACE: u64 = 0x6a6f_6c74_7063_7301; - impl PackingFamilyId { - pub fn physical_ref(&self) -> PackingFamilyRef { - let (id, index): (u64, u64) = match self { - Self::InstructionRa { index } => (0, *index as u64), - Self::BytecodeRa { index } => (1, *index as u64), - Self::RamRa { index } => (2, *index as u64), - Self::UnsignedIncChunk { index } => (3, *index as u64), - Self::UnsignedIncMsb => (4, 0), - Self::FieldRdIncByte { index } => (9, *index as u64), - Self::FieldRdIncSign => (10, 0), - Self::AdviceBytes { kind, index } => match kind { - PackingAdviceKind::Trusted => (11, *index as u64), - PackingAdviceKind::Untrusted => (12, *index as u64), - }, - Self::BytecodeChunk { index } => (13, *index as u64), - Self::ProgramImageInit => (14, 0), - Self::BytecodeRegisterSelector { chunk, selector } => { - (15, combine_two_indices(*chunk, *selector)) - } - Self::BytecodeCircuitFlag { chunk, flag } => (16, combine_two_indices(*chunk, *flag)), - Self::BytecodeInstructionFlag { chunk, flag } => { - (17, combine_two_indices(*chunk, *flag)) - } - Self::BytecodeLookupSelector { chunk } => (18, *chunk as u64), - Self::BytecodeRafFlag { chunk } => (19, *chunk as u64), - Self::BytecodeUnexpandedPcBytes { chunk } => (20, *chunk as u64), - Self::BytecodeImmBytes { chunk } => (21, *chunk as u64), - Self::Custom { namespace, index } => { - return PackingFamilyRef::new(u64::from(*namespace), 0, *index as u64); - } - }; - PackingFamilyRef::new(JOLT_PACKING_FAMILY_NAMESPACE, id, index) + pub const fn new(namespace: u64, id: u64, index: u64) -> Self { + Self { + namespace, + id, + index, + } } -} -fn combine_two_indices(left: usize, right: usize) -> u64 { - ((left as u64) << 32) | right as u64 + pub fn physical_ref(&self) -> PackingFamilyRef { + PackingFamilyRef::new(self.namespace, self.id, self.index) + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] @@ -615,12 +528,9 @@ impl PackingValidityRequirement { } } - pub fn bytecode_store_rd_disjoint(chunk: usize, store_flag: usize) -> Self { + pub fn bytecode_store_rd_disjoint(family: PackingFamilyId) -> Self { Self { - family: PackingFamilyId::BytecodeCircuitFlag { - chunk, - flag: store_flag, - }, + family, limbs: 1, alphabet_size: 2, kind: PackingValidityKind::BytecodeStoreRdDisjoint, @@ -999,76 +909,9 @@ fn layout_digest(families: &[PackingLayoutFamily], cells: usize, dimension: usiz } fn write_family_id(bytes: &mut Vec, id: &PackingFamilyId) { - match id { - PackingFamilyId::InstructionRa { index } => { - bytes.push(0); - write_usize(bytes, *index); - } - PackingFamilyId::BytecodeRa { index } => { - bytes.push(1); - write_usize(bytes, *index); - } - PackingFamilyId::RamRa { index } => { - bytes.push(2); - write_usize(bytes, *index); - } - PackingFamilyId::UnsignedIncChunk { index } => { - bytes.push(3); - write_usize(bytes, *index); - } - PackingFamilyId::UnsignedIncMsb => bytes.push(4), - PackingFamilyId::FieldRdIncByte { index } => { - bytes.push(9); - write_usize(bytes, *index); - } - PackingFamilyId::FieldRdIncSign => bytes.push(10), - PackingFamilyId::AdviceBytes { kind, index } => { - bytes.push(11); - bytes.push(advice_kind_tag(*kind)); - write_usize(bytes, *index); - } - PackingFamilyId::BytecodeChunk { index } => { - bytes.push(12); - write_usize(bytes, *index); - } - PackingFamilyId::ProgramImageInit => bytes.push(13), - PackingFamilyId::BytecodeRegisterSelector { chunk, selector } => { - bytes.push(15); - write_usize(bytes, *chunk); - write_usize(bytes, *selector); - } - PackingFamilyId::BytecodeCircuitFlag { chunk, flag } => { - bytes.push(16); - write_usize(bytes, *chunk); - write_usize(bytes, *flag); - } - PackingFamilyId::BytecodeInstructionFlag { chunk, flag } => { - bytes.push(17); - write_usize(bytes, *chunk); - write_usize(bytes, *flag); - } - PackingFamilyId::BytecodeLookupSelector { chunk } => { - bytes.push(18); - write_usize(bytes, *chunk); - } - PackingFamilyId::BytecodeRafFlag { chunk } => { - bytes.push(19); - write_usize(bytes, *chunk); - } - PackingFamilyId::BytecodeUnexpandedPcBytes { chunk } => { - bytes.push(20); - write_usize(bytes, *chunk); - } - PackingFamilyId::BytecodeImmBytes { chunk } => { - bytes.push(21); - write_usize(bytes, *chunk); - } - PackingFamilyId::Custom { namespace, index } => { - bytes.push(14); - bytes.extend_from_slice(&namespace.to_le_bytes()); - write_usize(bytes, *index); - } - } + bytes.extend_from_slice(&id.namespace.to_le_bytes()); + bytes.extend_from_slice(&id.id.to_le_bytes()); + bytes.extend_from_slice(&id.index.to_le_bytes()); } fn write_domain(bytes: &mut Vec, domain: PackingFactDomain) { @@ -1114,6 +957,16 @@ mod tests { use super::*; use jolt_field::{Fr, FromPrimitiveInt}; + const TEST_NAMESPACE: u64 = 0x7465_7374_7063_7301; + + fn family(id: u64, index: usize) -> PackingFamilyId { + PackingFamilyId::new(TEST_NAMESPACE, id, index as u64) + } + + fn pair_family(id: u64, left: usize, right: usize) -> PackingFamilyId { + PackingFamilyId::new(TEST_NAMESPACE, id, ((left as u64) << 32) | right as u64) + } + fn trace(log_t: usize) -> PackingFactDomain { PackingFactDomain::TraceRows { log_t } } @@ -1126,30 +979,28 @@ mod tests { PackingFamilySpec::direct(id, trace(log_t), 1, PackingAlphabet::Bit) } - fn base_ra_specs(log_t: usize) -> Vec { + fn many_byte_specs(log_t: usize) -> Vec { let mut specs = Vec::new(); - specs.extend( - (0..16).map(|index| byte_family(PackingFamilyId::InstructionRa { index }, log_t)), - ); - specs.extend((0..3).map(|index| byte_family(PackingFamilyId::BytecodeRa { index }, log_t))); - specs.extend((0..4).map(|index| byte_family(PackingFamilyId::RamRa { index }, log_t))); + specs.extend((0..16).map(|index| byte_family(family(0, index), log_t))); + specs.extend((0..3).map(|index| byte_family(family(1, index), log_t))); + specs.extend((0..4).map(|index| byte_family(family(2, index), log_t))); specs } - fn unsigned_increment_specs(log_t: usize) -> Vec { + fn extra_byte_and_bit_specs(log_t: usize) -> Vec { let mut specs = (0..8) - .map(|index| byte_family(PackingFamilyId::UnsignedIncChunk { index }, log_t)) + .map(|index| byte_family(family(3, index), log_t)) .collect::>(); - specs.push(bit_family(PackingFamilyId::UnsignedIncMsb, log_t)); + specs.push(bit_family(family(4, 0), log_t)); specs } #[test] fn packed_witness_layout_digest_stable() { let mut specs = vec![ - byte_family(PackingFamilyId::RamRa { index: 0 }, 4), - bit_family(PackingFamilyId::UnsignedIncMsb, 4), - byte_family(PackingFamilyId::InstructionRa { index: 0 }, 4), + byte_family(family(2, 0), 4), + bit_family(family(4, 0), 4), + byte_family(family(0, 0), 4), ]; let layout_a = PackingWitnessLayout::new(specs.clone()).expect("layout should build"); specs.reverse(); @@ -1161,10 +1012,7 @@ mod tests { #[test] fn packed_witness_layout_rejects_duplicate_ranges() { - let specs = vec![ - byte_family(PackingFamilyId::RamRa { index: 0 }, 3), - byte_family(PackingFamilyId::RamRa { index: 0 }, 3), - ]; + let specs = vec![byte_family(family(2, 0), 3), byte_family(family(2, 0), 3)]; assert!(matches!( PackingWitnessLayout::new(specs), Err(PackingLayoutError::DuplicateFamily { .. }) @@ -1172,8 +1020,8 @@ mod tests { } #[test] - fn large_trace_base_cells_are_5888_per_row() { - let layout = PackingWitnessLayout::new(base_ra_specs(20)).expect("layout should build"); + fn many_byte_families_report_trace_cells_per_row() { + let layout = PackingWitnessLayout::new(many_byte_specs(20)).expect("layout should build"); let audit = layout.audit(); assert_eq!(audit.trace_cells_per_row, Some(5_888)); @@ -1181,10 +1029,10 @@ mod tests { } #[test] - fn unsigned_increment_budget_is_n_plus_13() { + fn mixed_byte_and_bit_families_report_dimension() { let log_t = 20; - let mut specs = base_ra_specs(log_t); - specs.extend(unsigned_increment_specs(log_t)); + let mut specs = many_byte_specs(log_t); + specs.extend(extra_byte_and_bit_specs(log_t)); let layout = PackingWitnessLayout::new(specs).expect("layout should build"); let audit = layout.audit(); @@ -1195,8 +1043,8 @@ mod tests { #[test] fn bit_fact_costs_two_cells_per_row() { - let layout = PackingWitnessLayout::new([bit_family(PackingFamilyId::UnsignedIncMsb, 5)]) - .expect("layout should build"); + let layout = + PackingWitnessLayout::new([bit_family(family(4, 0), 5)]).expect("layout should build"); let audit = layout.audit(); assert_eq!(layout.cells, 64); @@ -1207,10 +1055,10 @@ mod tests { #[test] fn rank_unrank_roundtrip() { let layout = PackingWitnessLayout::new([ - byte_family(PackingFamilyId::RamRa { index: 0 }, 1), - bit_family(PackingFamilyId::UnsignedIncMsb, 1), + byte_family(family(2, 0), 1), + bit_family(family(4, 0), 1), PackingFamilySpec::direct( - PackingFamilyId::BytecodeChunk { index: 0 }, + family(13, 0), PackingFactDomain::BytecodeRows { log_bytecode: 1 }, 2, PackingAlphabet::Fixed { size: 3 }, @@ -1225,48 +1073,37 @@ mod tests { } #[test] - fn committed_bytecode_lane_families_are_distinct() { + fn families_with_distinct_physical_refs_do_not_alias() { let bytecode = PackingFactDomain::BytecodeRows { log_bytecode: 1 }; let families = [ - PackingFamilyId::BytecodeRegisterSelector { - chunk: 0, - selector: 0, - }, - PackingFamilyId::BytecodeRegisterSelector { - chunk: 0, - selector: 1, - }, - PackingFamilyId::BytecodeCircuitFlag { chunk: 0, flag: 0 }, - PackingFamilyId::BytecodeInstructionFlag { chunk: 0, flag: 0 }, - PackingFamilyId::BytecodeLookupSelector { chunk: 0 }, - PackingFamilyId::BytecodeRafFlag { chunk: 0 }, - PackingFamilyId::BytecodeUnexpandedPcBytes { chunk: 0 }, - PackingFamilyId::BytecodeImmBytes { chunk: 0 }, + pair_family(15, 0, 0), + pair_family(15, 0, 1), + pair_family(16, 0, 0), + pair_family(17, 0, 0), + family(18, 0), + family(19, 0), + family(20, 0), + family(21, 0), ]; let layout = PackingWitnessLayout::new([ PackingFamilySpec::direct( - families[0].clone(), + families[0], bytecode, 1, PackingAlphabet::Fixed { size: 32 }, ), PackingFamilySpec::direct( - families[1].clone(), + families[1], bytecode, 1, PackingAlphabet::Fixed { size: 32 }, ), - PackingFamilySpec::direct(families[2].clone(), bytecode, 1, PackingAlphabet::Bit), - PackingFamilySpec::direct(families[3].clone(), bytecode, 1, PackingAlphabet::Bit), - PackingFamilySpec::direct( - families[4].clone(), - bytecode, - 1, - PackingAlphabet::Fixed { size: 4 }, - ), - PackingFamilySpec::direct(families[5].clone(), bytecode, 1, PackingAlphabet::Bit), - PackingFamilySpec::direct(families[6].clone(), bytecode, 8, PackingAlphabet::Byte), - PackingFamilySpec::direct(families[7].clone(), bytecode, 16, PackingAlphabet::Byte), + PackingFamilySpec::direct(families[2], bytecode, 1, PackingAlphabet::Bit), + PackingFamilySpec::direct(families[3], bytecode, 1, PackingAlphabet::Bit), + PackingFamilySpec::direct(families[4], bytecode, 1, PackingAlphabet::Fixed { size: 4 }), + PackingFamilySpec::direct(families[5], bytecode, 1, PackingAlphabet::Bit), + PackingFamilySpec::direct(families[6], bytecode, 8, PackingAlphabet::Byte), + PackingFamilySpec::direct(families[7], bytecode, 16, PackingAlphabet::Byte), ]) .expect("layout should build"); @@ -1283,7 +1120,7 @@ mod tests { } let address = PackingCellAddress { - family: PackingFamilyId::BytecodeImmBytes { chunk: 0 }, + family: family(21, 0), row: 1, limb: 15, symbol: 7, @@ -1295,10 +1132,7 @@ mod tests { #[test] fn dummy_cells_are_zero_and_unreferenced() { let layout = PackingWitnessLayout::new([PackingFamilySpec::direct( - PackingFamilyId::Custom { - namespace: 1, - index: 0, - }, + family(0, 0), trace(0), 1, PackingAlphabet::Fixed { size: 3 }, @@ -1313,10 +1147,7 @@ mod tests { let source = SparsePackingWitness::::try_new(layout.clone(), Vec::new()) .expect("empty source should build"); let zero_address = PackingCellAddress { - family: PackingFamilyId::Custom { - namespace: 1, - index: 0, - }, + family: family(0, 0), row: 0, limb: 0, symbol: 2, @@ -1332,9 +1163,9 @@ mod tests { #[test] fn layout_sort_order_is_stable() { let layout = PackingWitnessLayout::new([ - byte_family(PackingFamilyId::RamRa { index: 3 }, 2), - byte_family(PackingFamilyId::InstructionRa { index: 1 }, 2), - byte_family(PackingFamilyId::BytecodeRa { index: 2 }, 2), + byte_family(family(2, 3), 2), + byte_family(family(0, 1), 2), + byte_family(family(1, 2), 2), ]) .expect("layout should build"); @@ -1344,11 +1175,7 @@ mod tests { .iter() .map(|family| &family.id) .collect::>(), - vec![ - &PackingFamilyId::InstructionRa { index: 1 }, - &PackingFamilyId::BytecodeRa { index: 2 }, - &PackingFamilyId::RamRa { index: 3 }, - ] + vec![&family(0, 1), &family(1, 2), &family(2, 3),] ); } @@ -1356,13 +1183,13 @@ mod tests { fn committed_program_families_use_non_trace_domains() { let layout = PackingWitnessLayout::new([ PackingFamilySpec::direct( - PackingFamilyId::BytecodeChunk { index: 0 }, + family(13, 0), PackingFactDomain::BytecodeRows { log_bytecode: 4 }, 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::ProgramImageInit, + family(14, 0), PackingFactDomain::ProgramImageWords { log_words: 3 }, 8, PackingAlphabet::Byte, @@ -1380,13 +1207,10 @@ mod tests { #[test] fn planner_audit_fields_are_reported() { let layout = PackingWitnessLayout::new([ - byte_family(PackingFamilyId::InstructionRa { index: 0 }, 2), - bit_family(PackingFamilyId::UnsignedIncMsb, 2), + byte_family(family(0, 0), 2), + bit_family(family(4, 0), 2), PackingFamilySpec::direct( - PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Trusted, - index: 0, - }, + family(11, 0), PackingFactDomain::AdviceBytes { kind: PackingAdviceKind::Trusted, log_bytes: 1, @@ -1414,19 +1238,17 @@ mod tests { #[test] fn packed_witness_source_respects_layout() { - let layout = PackingWitnessLayout::new([ - byte_family(PackingFamilyId::RamRa { index: 0 }, 1), - bit_family(PackingFamilyId::UnsignedIncMsb, 1), - ]) - .expect("layout should build"); + let layout = + PackingWitnessLayout::new([byte_family(family(2, 0), 1), bit_family(family(4, 0), 1)]) + .expect("layout should build"); let one_address = PackingCellAddress { - family: PackingFamilyId::RamRa { index: 0 }, + family: family(2, 0), row: 1, limb: 0, symbol: 17, }; let sign_address = PackingCellAddress { - family: PackingFamilyId::UnsignedIncMsb, + family: family(4, 0), row: 0, limb: 0, symbol: 1, @@ -1481,22 +1303,21 @@ mod tests { #[test] fn view_catalog_references_existing_families() { let layout = - PackingWitnessLayout::new([byte_family(PackingFamilyId::RamRa { index: 0 }, 2)]) - .expect("layout should build"); + PackingWitnessLayout::new([byte_family(family(2, 0), 2)]).expect("layout should build"); layout - .validate_view_families(&[PackingFamilyId::RamRa { index: 0 }]) + .validate_view_families(&[family(2, 0)]) .expect("existing family should validate"); assert!(matches!( - layout.validate_view_families(&[PackingFamilyId::UnsignedIncMsb]), + layout.validate_view_families(&[family(4, 0)]), Err(PackingLayoutError::MissingViewFamily { .. }) )); } #[test] fn sparse_source_rejects_out_of_layout_ranks() { - let layout = PackingWitnessLayout::new([bit_family(PackingFamilyId::UnsignedIncMsb, 0)]) - .expect("layout should build"); + let layout = + PackingWitnessLayout::new([bit_family(family(4, 0), 0)]).expect("layout should build"); assert!(matches!( SparsePackingWitness::try_new(layout.clone(), vec![(layout.cells, Fr::from_u64(1))]), diff --git a/crates/jolt-openings/src/packing_view.rs b/crates/jolt-openings/src/packing_view.rs index dbdd48f5b4..2820480365 100644 --- a/crates/jolt-openings/src/packing_view.rs +++ b/crates/jolt-openings/src/packing_view.rs @@ -3,8 +3,8 @@ use std::error::Error; use std::fmt::{self, Display, Formatter}; use crate::{ - PackingAdviceKind, PackingCellAddress, PackingFactDomain, PackingFamilyId, PackingLayoutError, - PackingTerm, PackingWitnessLayout, PackingWitnessSource, PhysicalView, + PackingCellAddress, PackingFactDomain, PackingFamilyId, PackingLayoutError, PackingTerm, + PackingWitnessLayout, PackingWitnessSource, PhysicalView, }; use blake2::digest::consts::U32; use blake2::{Blake2b, Digest}; @@ -213,7 +213,7 @@ impl PackingViewFormula { symbol, } => { let address = PackingCellAddress { - family: family.clone(), + family: *family, row, limb: *limb, symbol: *symbol, @@ -223,7 +223,7 @@ impl PackingViewFormula { Self::LinearDecoded { terms, .. } | Self::ReducedMasked { terms } => { for term in terms { let address = PackingCellAddress { - family: term.family.clone(), + family: term.family, row, limb: term.limb, symbol: term.symbol, @@ -285,14 +285,14 @@ impl PackingViewFormula { layout: &PackingWitnessLayout, ) -> Result { match self { - Self::Direct { family, .. } => layout - .family(family) - .map(|family| family.domain) - .ok_or_else(|| { - PackingViewError::Layout(PackingLayoutError::MissingFamily { - id: family.clone(), - }) - }), + Self::Direct { family, .. } => { + layout + .family(family) + .map(|family| family.domain) + .ok_or(PackingViewError::Layout( + PackingLayoutError::MissingFamily { id: *family }, + )) + } Self::LinearDecoded { terms, .. } | Self::ReducedMasked { terms } => { let Some(term) = terms.first() else { return Err(PackingViewError::EmptyLinearView); @@ -300,11 +300,9 @@ impl PackingViewFormula { layout .family(&term.family) .map(|family| family.domain) - .ok_or_else(|| { - PackingViewError::Layout(PackingLayoutError::MissingFamily { - id: term.family.clone(), - }) - }) + .ok_or(PackingViewError::Layout( + PackingLayoutError::MissingFamily { id: term.family }, + )) } Self::MaskedDecoded => Err(PackingViewError::MaskedViewRequiresTranslation), } @@ -318,12 +316,12 @@ impl PackingViewFormula { limb, symbol, } => { - let _ = coefficients.insert((family.clone(), *limb, *symbol), F::one()); + let _ = coefficients.insert((*family, *limb, *symbol), F::one()); } Self::LinearDecoded { terms, .. } | Self::ReducedMasked { terms } => { for term in terms { *coefficients - .entry((term.family.clone(), term.limb, term.symbol)) + .entry((term.family, term.limb, term.symbol)) .or_insert_with(F::zero) += term.coefficient; } } @@ -445,12 +443,10 @@ fn validate_term_shape( ) -> Result { let family = layout .family(family_id) - .ok_or_else(|| PackingLayoutError::MissingFamily { - id: family_id.clone(), - })?; + .ok_or(PackingLayoutError::MissingFamily { id: *family_id })?; if limb >= family.limbs || symbol >= family.alphabet.size() { return Err(PackingLayoutError::AddressOutOfRange { - family: family_id.clone(), + family: *family_id, row: 0, limb, symbol, @@ -531,79 +527,9 @@ fn write_validity(bytes: &mut Vec, validity: PackingViewValidity) { } fn write_family_id(bytes: &mut Vec, id: &PackingFamilyId) { - match id { - PackingFamilyId::InstructionRa { index } => { - bytes.push(0); - write_usize(bytes, *index); - } - PackingFamilyId::BytecodeRa { index } => { - bytes.push(1); - write_usize(bytes, *index); - } - PackingFamilyId::RamRa { index } => { - bytes.push(2); - write_usize(bytes, *index); - } - PackingFamilyId::UnsignedIncChunk { index } => { - bytes.push(3); - write_usize(bytes, *index); - } - PackingFamilyId::UnsignedIncMsb => bytes.push(4), - PackingFamilyId::FieldRdIncByte { index } => { - bytes.push(9); - write_usize(bytes, *index); - } - PackingFamilyId::FieldRdIncSign => bytes.push(10), - PackingFamilyId::AdviceBytes { kind, index } => { - bytes.push(11); - bytes.push(match kind { - PackingAdviceKind::Trusted => 0, - PackingAdviceKind::Untrusted => 1, - }); - write_usize(bytes, *index); - } - PackingFamilyId::BytecodeChunk { index } => { - bytes.push(12); - write_usize(bytes, *index); - } - PackingFamilyId::ProgramImageInit => bytes.push(13), - PackingFamilyId::BytecodeRegisterSelector { chunk, selector } => { - bytes.push(15); - write_usize(bytes, *chunk); - write_usize(bytes, *selector); - } - PackingFamilyId::BytecodeCircuitFlag { chunk, flag } => { - bytes.push(16); - write_usize(bytes, *chunk); - write_usize(bytes, *flag); - } - PackingFamilyId::BytecodeInstructionFlag { chunk, flag } => { - bytes.push(17); - write_usize(bytes, *chunk); - write_usize(bytes, *flag); - } - PackingFamilyId::BytecodeLookupSelector { chunk } => { - bytes.push(18); - write_usize(bytes, *chunk); - } - PackingFamilyId::BytecodeRafFlag { chunk } => { - bytes.push(19); - write_usize(bytes, *chunk); - } - PackingFamilyId::BytecodeUnexpandedPcBytes { chunk } => { - bytes.push(20); - write_usize(bytes, *chunk); - } - PackingFamilyId::BytecodeImmBytes { chunk } => { - bytes.push(21); - write_usize(bytes, *chunk); - } - PackingFamilyId::Custom { namespace, index } => { - bytes.push(14); - bytes.extend_from_slice(&namespace.to_le_bytes()); - write_usize(bytes, *index); - } - } + bytes.extend_from_slice(&id.namespace.to_le_bytes()); + bytes.extend_from_slice(&id.id.to_le_bytes()); + bytes.extend_from_slice(&id.index.to_le_bytes()); } fn write_field(bytes: &mut Vec, value: F) { @@ -630,6 +556,11 @@ mod tests { }; use jolt_field::{Fr, FromPrimitiveInt}; + const TEST_NAMESPACE: u64 = 0x7465_7374_7669_6577; + const BYTE_FAMILY: PackingFamilyId = PackingFamilyId::new(TEST_NAMESPACE, 0, 0); + const BIT_FAMILY: PackingFamilyId = PackingFamilyId::new(TEST_NAMESPACE, 1, 0); + const PROGRAM_FAMILY: PackingFamilyId = PackingFamilyId::new(TEST_NAMESPACE, 2, 0); + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] enum OpeningId { A, @@ -648,13 +579,13 @@ mod tests { fn byte_layout() -> PackingWitnessLayout { PackingWitnessLayout::new([ PackingFamilySpec::direct( - PackingFamilyId::RamRa { index: 0 }, + BYTE_FAMILY, PackingFactDomain::TraceRows { log_t: 1 }, 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncMsb, + BIT_FAMILY, PackingFactDomain::TraceRows { log_t: 1 }, 1, PackingAlphabet::Bit, @@ -665,7 +596,7 @@ mod tests { fn byte_decode_terms(family: PackingFamilyId) -> Vec> { (0..256) - .map(|symbol| PackingViewTerm::new(f(symbol as u64), family.clone(), 0, symbol)) + .map(|symbol| PackingViewTerm::new(f(symbol as u64), family, 0, symbol)) .collect() } @@ -673,14 +604,14 @@ mod tests { fn direct_view_translation_matches_packed_eval() { let layout = byte_layout(); let address = PackingCellAddress { - family: PackingFamilyId::UnsignedIncMsb, + family: BIT_FAMILY, row: 1, limb: 0, symbol: 1, }; let source = SparsePackingWitness::try_from_cells(layout.clone(), [(address, f(1))]) .expect("source should build"); - let formula = PackingViewFormula::::direct(PackingFamilyId::UnsignedIncMsb, 0, 1); + let formula = PackingViewFormula::::direct(BIT_FAMILY, 0, 1); assert_eq!( formula.eval_row(&source, 1).expect("view should evaluate"), @@ -693,7 +624,7 @@ mod tests { PhysicalView::Packing { terms, .. } if terms.len() == 1 && terms[0].coefficient == f(1) - && terms[0].family == PackingFamilyId::UnsignedIncMsb.physical_ref() + && terms[0].family == BIT_FAMILY.physical_ref() && terms[0].symbol == 1 )); } @@ -702,17 +633,14 @@ mod tests { fn linear_decode_translation_matches_direct_sum() { let layout = byte_layout(); let address = PackingCellAddress { - family: PackingFamilyId::RamRa { index: 0 }, + family: BYTE_FAMILY, row: 0, limb: 0, symbol: 7, }; let source = SparsePackingWitness::try_from_cells(layout.clone(), [(address, f(1))]) .expect("source should build"); - let formula = - PackingViewFormula::linear_decoded(byte_decode_terms(PackingFamilyId::RamRa { - index: 0, - })); + let formula = PackingViewFormula::linear_decoded(byte_decode_terms(BYTE_FAMILY)); assert_eq!( formula.eval_row(&source, 0).expect("view should evaluate"), @@ -726,7 +654,7 @@ mod tests { if layout_digest == layout.digest && terms.len() == 256 && terms[7].coefficient == f(7) - && terms[7].family == (PackingFamilyId::RamRa { index: 0 }).physical_ref() + && terms[7].family == BYTE_FAMILY.physical_ref() && terms[7].symbol == 7 )); } @@ -735,14 +663,14 @@ mod tests { fn direct_view_point_eval_interpolates_rows() { let layout = byte_layout(); let address = PackingCellAddress { - family: PackingFamilyId::UnsignedIncMsb, + family: BIT_FAMILY, row: 1, limb: 0, symbol: 1, }; let source = SparsePackingWitness::try_from_cells(layout, [(address, f(1))]) .expect("source should build"); - let formula = PackingViewFormula::::direct(PackingFamilyId::UnsignedIncMsb, 0, 1); + let formula = PackingViewFormula::::direct(BIT_FAMILY, 0, 1); let point = [f(3)]; assert_eq!( @@ -761,7 +689,7 @@ mod tests { [ ( PackingCellAddress { - family: PackingFamilyId::RamRa { index: 0 }, + family: BYTE_FAMILY, row: 0, limb: 0, symbol: 7, @@ -770,7 +698,7 @@ mod tests { ), ( PackingCellAddress { - family: PackingFamilyId::RamRa { index: 0 }, + family: BYTE_FAMILY, row: 1, limb: 0, symbol: 11, @@ -780,10 +708,7 @@ mod tests { ], ) .expect("source should build"); - let formula = - PackingViewFormula::linear_decoded(byte_decode_terms(PackingFamilyId::RamRa { - index: 0, - })); + let formula = PackingViewFormula::linear_decoded(byte_decode_terms(BYTE_FAMILY)); let point = [f(5)]; let expected = (f(1) - point[0]) * f(7) + point[0] * f(11); @@ -802,7 +727,7 @@ mod tests { layout, [( PackingCellAddress { - family: PackingFamilyId::UnsignedIncMsb, + family: BIT_FAMILY, row: 1, limb: 0, symbol: 1, @@ -811,7 +736,7 @@ mod tests { )], ) .expect("source should build"); - let formula = PackingViewFormula::::direct(PackingFamilyId::UnsignedIncMsb, 0, 1); + let formula = PackingViewFormula::::direct(BIT_FAMILY, 0, 1); assert!(matches!( formula.eval_row_point(&source, &[]), @@ -841,7 +766,7 @@ mod tests { [PackingViewEntry::new( OpeningId::A, RelationId::First, - PackingViewFormula::::direct(PackingFamilyId::UnsignedIncMsb, 0, 1), + PackingViewFormula::::direct(BIT_FAMILY, 0, 1), )], ) .expect("catalog should build"); @@ -850,7 +775,7 @@ mod tests { [PackingViewEntry::new( OpeningId::A, RelationId::First, - PackingViewFormula::::direct(PackingFamilyId::UnsignedIncMsb, 0, 0), + PackingViewFormula::::direct(BIT_FAMILY, 0, 0), )], ) .expect("catalog should build"); @@ -865,9 +790,7 @@ mod tests { #[test] fn decoded_view_without_validity_rejects_or_is_not_enabled() { let layout = byte_layout(); - let formula = PackingViewFormula::unchecked_linear_decoded(byte_decode_terms( - PackingFamilyId::RamRa { index: 0 }, - )); + let formula = PackingViewFormula::unchecked_linear_decoded(byte_decode_terms(BYTE_FAMILY)); assert!(matches!( formula.physical_view(&layout), @@ -884,12 +807,12 @@ mod tests { PackingViewEntry::new( OpeningId::A, RelationId::Second, - PackingViewFormula::::direct(PackingFamilyId::UnsignedIncMsb, 0, 0), + PackingViewFormula::::direct(BIT_FAMILY, 0, 0), ), PackingViewEntry::new( OpeningId::A, RelationId::First, - PackingViewFormula::::direct(PackingFamilyId::UnsignedIncMsb, 0, 1), + PackingViewFormula::::direct(BIT_FAMILY, 0, 1), ), ], ) @@ -899,33 +822,31 @@ mod tests { catalog .lookup(&OpeningId::A, &RelationId::First) .expect("first relation should exist"), - &PackingViewFormula::::direct(PackingFamilyId::UnsignedIncMsb, 0, 1) + &PackingViewFormula::::direct(BIT_FAMILY, 0, 1) ); assert_eq!( catalog .lookup(&OpeningId::A, &RelationId::Second) .expect("second relation should exist"), - &PackingViewFormula::::direct(PackingFamilyId::UnsignedIncMsb, 0, 0) + &PackingViewFormula::::direct(BIT_FAMILY, 0, 0) ); } #[test] fn bound_precommitted_program_view_formula_validates_against_supplied_layout() { let layout = PackingWitnessLayout::new([PackingFamilySpec::direct( - PackingFamilyId::ProgramImageInit, + PROGRAM_FAMILY, PackingFactDomain::ProgramImageWords { log_words: 2 }, 8, PackingAlphabet::Byte, )]) .expect("layout should build"); - let formula = PackingViewFormula::linear_decoded(byte_decode_terms( - PackingFamilyId::ProgramImageInit, - )); + let formula = PackingViewFormula::linear_decoded(byte_decode_terms(PROGRAM_FAMILY)); formula.validate(&layout).expect("formula should validate"); assert_eq!( layout - .family(&PackingFamilyId::ProgramImageInit) + .family(&PROGRAM_FAMILY) .expect("program family should exist") .domain, PackingFactDomain::ProgramImageWords { log_words: 2 } diff --git a/crates/jolt-verifier/src/akita.rs b/crates/jolt-verifier/src/akita.rs index df3a6818d6..c3ba17c4f4 100644 --- a/crates/jolt-verifier/src/akita.rs +++ b/crates/jolt-verifier/src/akita.rs @@ -1,7 +1,7 @@ //! Prover-facing helpers for assembling Akita verifier artifacts. use crate::{ - akita_packing::AkitaPackingScheme, + akita_packing::{self, AkitaPackingScheme}, akita_validation::{ validate_akita_advice_commitment_aliases, validate_akita_packing_opening_proof_payload_shape, @@ -19,10 +19,14 @@ use crate::{ VerifierError, }; use common::jolt_device::JoltDevice; -use jolt_akita::{AkitaBatchProof, AkitaCommitment, AkitaField, AkitaProverHint}; +use jolt_akita::{ + AkitaBatchProof, AkitaCommitment, AkitaField, AkitaProverHint, AkitaProverSetup, AkitaScheme, + AkitaVerifierSetup, +}; use jolt_field::{RingAccumulator, WithAccumulator}; use jolt_openings::{ - CommitmentScheme, PackingBatchProof, PackingWitnessLayout, PackingWitnessSource, + PackingBatch, PackingBatchProof, PackingProverSetup, PackingVerifierSetup, + PackingWitnessLayout, PackingWitnessSource, }; use jolt_transcript::Transcript; @@ -41,11 +45,14 @@ pub use crate::akita_witness::{build_akita_packing_jolt_witness, AkitaPackingJol pub type AkitaClearVectorCommitment = ClearOnlyVectorCommitment; pub type AkitaPackingBatchProof = PackingBatchProof; -pub type AkitaPackingProverSetup = ::ProverSetup; -pub type AkitaPackingVerifierSetup = ::VerifierSetup; -pub type AkitaVerifierPreprocessing = - JoltVerifierPreprocessing; -pub type AkitaJoltProof = JoltProof; +pub type AkitaPackingProverSetup = PackingProverSetup; +pub type AkitaPackingVerifierSetup = PackingVerifierSetup; +pub type AkitaVerifierPreprocessing = JoltVerifierPreprocessing< + PackingBatch, + AkitaClearVectorCommitment, +>; +pub type AkitaJoltProof = + JoltProof, AkitaClearVectorCommitment>; #[derive(Clone, Debug)] pub struct AkitaPackingWitnessArtifacts { @@ -83,7 +90,7 @@ pub fn commit_akita_packing_witness( where S: PackingWitnessSource, { - let protocol = lattice_protocol_config_for_packed_witness_layout(source.layout()); + let protocol = lattice_protocol_config_for_packed_witness_layout(source.layout())?; commit_akita_packing_witness_with_config(protocol, setup, source) } @@ -98,7 +105,7 @@ where let layout = source.layout().clone(); validate_lattice_packed_witness_layout_config(&protocol, &layout)?; let (commitment, hint) = - AkitaPackingScheme::commit_packing_source(setup, source).map_err(|error| { + akita_packing::commit_packing_source(setup, source).map_err(|error| { VerifierError::LatticePackingCommitmentFailed { reason: error.to_string(), } diff --git a/crates/jolt-verifier/src/akita_openings.rs b/crates/jolt-verifier/src/akita_openings.rs index de5702bd7b..d54fd80608 100644 --- a/crates/jolt-verifier/src/akita_openings.rs +++ b/crates/jolt-verifier/src/akita_openings.rs @@ -3,7 +3,7 @@ use crate::{ AkitaClearVectorCommitment, AkitaJoltProof, AkitaPackingBatchProof, AkitaPackingProverSetup, AkitaPackingWitnessArtifacts, AkitaVerifierPreprocessing, }, - akita_packing::AkitaPackingScheme, + akita_packing::{self, AkitaPackingScheme}, akita_validation::validate_akita_artifacts_for_proof, akita_validity::{attach_akita_packing_validity_proof, prove_akita_jolt_packed_validity}, stages::stage8::{Stage8BatchStatement, Stage8OpeningId}, @@ -67,7 +67,7 @@ where } } - AkitaPackingScheme::prove_packing_source_batch( + akita_packing::prove_packing_source_batch( setup, transcript, statement, @@ -429,7 +429,8 @@ mod tests { }; use jolt_akita::{AkitaScheme, AkitaSetupParams, AKITA_FIELD_MODULUS}; use jolt_claims::protocols::jolt::{ - unsigned_inc_msb_opening, JoltCommittedPolynomial, JoltOpeningId, JoltRelationId, + unsigned_inc_msb_opening, JoltCommittedPolynomial, JoltOpeningId, JoltPackingFamilyId, + JoltRelationId, }; use jolt_field::FixedByteSize; use jolt_openings::{ @@ -440,22 +441,39 @@ mod tests { use jolt_poly::Point; use jolt_transcript::{Blake2bTranscript, Transcript}; + fn physical(family: JoltPackingFamilyId) -> PackingFamilyId { + family.into() + } + fn tiny_layout() -> jolt_openings::PackingWitnessLayout { - jolt_openings::PackingWitnessLayout::new([ + let specs = vec![ PackingFamilySpec::direct( - PackingFamilyId::InstructionRa { index: 0 }, + physical(JoltPackingFamilyId::InstructionRa { index: 0 }), PackingFactDomain::TraceRows { log_t: 0 }, 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncMsb, + physical(JoltPackingFamilyId::UnsignedIncMsb), PackingFactDomain::TraceRows { log_t: 0 }, 1, PackingAlphabet::Bit, ), - ]) - .expect("layout should build") + ]; + #[cfg(feature = "field-inline")] + let specs = { + let mut specs = specs; + specs.extend((0..AkitaField::NUM_BYTES).map(|index| { + PackingFamilySpec::direct( + physical(JoltPackingFamilyId::FieldRdIncByte { index }), + PackingFactDomain::TraceRows { log_t: 0 }, + 1, + PackingAlphabet::Byte, + ) + })); + specs + }; + jolt_openings::PackingWitnessLayout::new(specs).expect("layout should build") } fn packed_cell(family: PackingFamilyId, symbol: usize) -> PackingCellAddress { @@ -486,16 +504,13 @@ mod tests { let layout = tiny_layout(); let params = akita_packing_params(&layout, 1); let (prover_setup, verifier_setup) = AkitaPackingScheme::setup(params); - let instruction_family = PackingFamilyId::InstructionRa { index: 0 }; - let sign_family = PackingFamilyId::UnsignedIncMsb; + let instruction_family = physical(JoltPackingFamilyId::InstructionRa { index: 0 }); + let sign_family = physical(JoltPackingFamilyId::UnsignedIncMsb); let source = SparsePackingWitness::try_from_cells( layout.clone(), [ - ( - packed_cell(instruction_family.clone(), 7), - AkitaField::one(), - ), - (packed_cell(sign_family.clone(), 1), AkitaField::one()), + (packed_cell(instruction_family, 7), AkitaField::one()), + (packed_cell(sign_family, 1), AkitaField::one()), ], ) .expect("source should build"); @@ -687,10 +702,10 @@ mod tests { let layout = tiny_layout(); let params = akita_packing_params(&layout, 1); let (prover_setup, verifier_setup) = AkitaPackingScheme::setup(params); - let sign_family = PackingFamilyId::UnsignedIncMsb; + let sign_family = physical(JoltPackingFamilyId::UnsignedIncMsb); let source = SparsePackingWitness::try_from_cells( layout.clone(), - [(packed_cell(sign_family.clone(), 1), AkitaField::one())], + [(packed_cell(sign_family, 1), AkitaField::one())], ) .expect("source should build"); let artifact = commit_akita_packing_witness(&prover_setup, &source) diff --git a/crates/jolt-verifier/src/akita_packing.rs b/crates/jolt-verifier/src/akita_packing.rs index 7c1ba50f7a..a94953b8db 100644 --- a/crates/jolt-verifier/src/akita_packing.rs +++ b/crates/jolt-verifier/src/akita_packing.rs @@ -1,279 +1,98 @@ use std::collections::BTreeSet; use jolt_akita::{ - AkitaBatchProof, AkitaCommitment, AkitaField, AkitaHidingCommitment, AkitaProverHint, - AkitaProverSetup, AkitaScheme, AkitaSetupParams, AkitaSparsePolynomial, AkitaVerifierSetup, - AKITA_D, + AkitaBatchProof, AkitaCommitment, AkitaField, AkitaProverHint, AkitaProverSetup, AkitaScheme, + AkitaSparsePolynomial, AKITA_D, }; -use jolt_crypto::Commitment; use jolt_openings::{ has_packing_view, packing_witness_source_polynomial, prove_sparse_packing_reduction, validate_packing_source_dimension, validate_packing_source_layout, validate_packing_statement, - BatchOpeningResult, BatchOpeningScheme, BatchOpeningStatement, CommitmentScheme, OpeningsError, - PackingBatch, PackingBatchProof, PackingProverSetup, PackingSetupParams, PackingSource, - PackingVerifierSetup, PackingWitnessLayout, PackingWitnessSource, ZkBatchOpeningScheme, - ZkOpeningScheme, + BatchOpeningScheme, BatchOpeningStatement, OpeningsError, PackingBatch, PackingBatchProof, + PackingProverSetup, PackingSource, PackingWitnessLayout, PackingWitnessSource, }; -use jolt_poly::{MultilinearPoly, Polynomial}; -use jolt_transcript::{AppendToTranscript, Label, Transcript}; -use serde::{Deserialize, Serialize}; +use jolt_transcript::Transcript; -type AkitaPackingAdapter = PackingBatch; +pub(crate) type AkitaPackingScheme = PackingBatch; +pub(crate) type AkitaPackingBatchProof = PackingBatchProof; +pub(crate) type AkitaPackingProverSetup = + PackingProverSetup; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct AkitaPackingScheme; - -impl AkitaPackingScheme { - pub fn commit_packing_source( - setup: &::ProverSetup, - source: &S, - ) -> Result<(AkitaCommitment, AkitaProverHint), OpeningsError> - where - S: PackingWitnessSource, - { - validate_packing_source_dimension(setup.pcs.max_num_vars, source.layout())?; - if let Some(polynomial) = packed_source_sparse_polynomial(source)? { - return AkitaScheme::commit_sparse_polynomial( - &setup.pcs, - source.layout().digest, - &polynomial, - ); - } - let polynomial = packing_witness_source_polynomial(source)?; - AkitaScheme::commit_group(&setup.pcs, source.layout().digest, &[polynomial]) - } - - pub fn prove_packing_source_batch( - setup: &::ProverSetup, - transcript: &mut T, - statement: &BatchOpeningStatement, - source: &S, - hint: AkitaProverHint, - ) -> Result, OpeningsError> - where - T: Transcript, - S: PackingWitnessSource, - { - validate_packing_source_dimension(setup.pcs.max_num_vars, source.layout())?; - if let Some(sparse_polynomial) = packed_source_sparse_polynomial(source)? { - if !has_packing_view(statement) { - let native = AkitaScheme::prove_sparse_batch( - &setup.pcs, - transcript, - statement, - &sparse_polynomial, - hint, - )?; - return Ok(PackingBatchProof { - reduction: None, - native, - }); - } +pub(crate) fn commit_packing_source( + setup: &AkitaPackingProverSetup, + source: &S, +) -> Result<(AkitaCommitment, AkitaProverHint), OpeningsError> +where + S: PackingWitnessSource, +{ + validate_packing_source_dimension(setup.pcs.max_num_vars, source.layout())?; + if let Some(polynomial) = packed_source_sparse_polynomial(source)? { + return AkitaScheme::commit_sparse_polynomial( + &setup.pcs, + source.layout().digest, + &polynomial, + ); + } + let polynomial = packing_witness_source_polynomial(source)?; + AkitaScheme::commit_group(&setup.pcs, source.layout().digest, &[polynomial]) +} - let shape = validate_packed_source_prover_inputs(setup, statement, source, &hint)?; - let source = AkitaPackingSource(source); - let reduction = - prove_sparse_packing_reduction(shape.layout, statement, &source, transcript)?; - let native_statement = singleton_statement( - shape.commitment.clone(), - &reduction.opening_point, - reduction.opening_eval, - ); +pub(crate) fn prove_packing_source_batch( + setup: &AkitaPackingProverSetup, + transcript: &mut T, + statement: &BatchOpeningStatement, + source: &S, + hint: AkitaProverHint, +) -> Result +where + T: Transcript, + S: PackingWitnessSource, +{ + validate_packing_source_dimension(setup.pcs.max_num_vars, source.layout())?; + if let Some(sparse_polynomial) = packed_source_sparse_polynomial(source)? { + if !has_packing_view(statement) { let native = AkitaScheme::prove_sparse_batch( &setup.pcs, transcript, - &native_statement, + statement, &sparse_polynomial, hint, )?; return Ok(PackingBatchProof { - reduction: Some(reduction.proof), + reduction: None, native, }); } - let polynomial = packing_witness_source_polynomial(source)?; - ::prove_batch( - setup, - transcript, - statement, - std::slice::from_ref(&polynomial), - vec![hint], - ) - } -} - -impl Commitment for AkitaPackingScheme { - type Output = AkitaCommitment; -} - -impl CommitmentScheme for AkitaPackingScheme { - type Field = AkitaField; - type Proof = PackingBatchProof; - type ProverSetup = PackingProverSetup; - type VerifierSetup = PackingVerifierSetup; - type Polynomial = Polynomial; - type OpeningHint = AkitaProverHint; - type SetupParams = PackingSetupParams; - - fn setup(params: Self::SetupParams) -> (Self::ProverSetup, Self::VerifierSetup) { - ::setup(params) - } - - fn verifier_setup(prover_setup: &Self::ProverSetup) -> Self::VerifierSetup { - ::verifier_setup(prover_setup) - } - - fn commit + ?Sized>( - _poly: &P, - _setup: &Self::ProverSetup, - ) -> (Self::Output, Self::OpeningHint) { - unsupported_dense_packed_path("commit") - } - - fn open( - _poly: &Self::Polynomial, - _point: &[Self::Field], - _eval: Self::Field, - _setup: &Self::ProverSetup, - _hint: Option, - _transcript: &mut impl Transcript, - ) -> Self::Proof { - unsupported_dense_packed_path("open") - } - - fn verify( - commitment: &Self::Output, - point: &[Self::Field], - eval: Self::Field, - proof: &Self::Proof, - setup: &Self::VerifierSetup, - transcript: &mut impl Transcript, - ) -> Result<(), OpeningsError> { - ::verify( - commitment, point, eval, proof, setup, transcript, - ) - } - - fn bind_opening_inputs( - transcript: &mut impl Transcript, - point: &[Self::Field], - eval: &Self::Field, - ) { - AkitaScheme::bind_opening_inputs(transcript, point, eval); - } -} - -impl BatchOpeningScheme for AkitaPackingScheme { - fn prove_batch( - setup: &Self::ProverSetup, - transcript: &mut T, - statement: &BatchOpeningStatement, - polynomials: &[Self::Polynomial], - hints: Vec, - ) -> Result - where - T: Transcript, - { - if has_packing_view(statement) { - validate_packed_adapter_prover_inputs(setup, statement, polynomials, &hints)?; - } - ::prove_batch( - setup, + let shape = validate_packed_source_prover_inputs(setup, statement, source, &hint)?; + let source = AkitaPackingSource(source); + let reduction = + prove_sparse_packing_reduction(shape.layout, statement, &source, transcript)?; + let native_statement = singleton_statement( + shape.commitment.clone(), + &reduction.opening_point, + reduction.opening_eval, + ); + let native = AkitaScheme::prove_sparse_batch( + &setup.pcs, transcript, - statement, - polynomials, - hints, - ) - } - - fn verify_batch( - setup: &Self::VerifierSetup, - transcript: &mut T, - statement: &BatchOpeningStatement, - proof: &Self::Proof, - ) -> Result, OpeningsError> - where - T: Transcript, - { - if has_packing_view(statement) { - validate_packed_adapter_verifier_inputs(setup, statement)?; - } - ::verify_batch( - setup, transcript, statement, proof, - ) - } -} - -impl ZkOpeningScheme for AkitaPackingScheme { - type HidingCommitment = AkitaHidingCommitment; - type Blind = (); - - fn commit_zk + ?Sized>( - _poly: &P, - _setup: &Self::ProverSetup, - ) -> (Self::Output, Self::OpeningHint) { - unsupported_dense_packed_path("commit_zk") - } - - fn open_zk( - _poly: &Self::Polynomial, - _point: &[Self::Field], - _eval: Self::Field, - _setup: &Self::ProverSetup, - _hint: Self::OpeningHint, - _transcript: &mut impl Transcript, - ) -> (Self::Proof, Self::HidingCommitment, Self::Blind) { - unsupported_dense_packed_path("open_zk") - } - - fn verify_zk( - _commitment: &Self::Output, - _point: &[Self::Field], - _proof: &Self::Proof, - _setup: &Self::VerifierSetup, - _transcript: &mut impl Transcript, - ) -> Result { - Err(transparent_zk_error()) - } - - fn bind_zk_opening_inputs( - transcript: &mut impl Transcript, - point: &[Self::Field], - hiding_commitment: &Self::HidingCommitment, - ) { - transcript.append(&Label(b"akpk_zk_inputs")); - append_field_slice(transcript, b"akpk_zk_point", point); - hiding_commitment.append_to_transcript(transcript); - } -} - -impl ZkBatchOpeningScheme for AkitaPackingScheme { - fn prove_batch_zk( - _setup: &Self::ProverSetup, - _transcript: &mut T, - _statement: &BatchOpeningStatement, - _evals: &[Self::Field], - _polynomials: &[Self::Polynomial], - _hints: Vec, - ) -> Result<(Self::Proof, Self::HidingCommitment, Self::Blind), OpeningsError> - where - T: Transcript, - { - Err(transparent_zk_error()) + &native_statement, + &sparse_polynomial, + hint, + )?; + return Ok(PackingBatchProof { + reduction: Some(reduction.proof), + native, + }); } - fn verify_batch_zk( - _setup: &Self::VerifierSetup, - _transcript: &mut T, - _statement: &BatchOpeningStatement, - _proof: &Self::Proof, - ) -> Result, OpeningsError> - where - T: Transcript, - { - Err(transparent_zk_error()) - } + let polynomial = packing_witness_source_polynomial(source)?; + ::prove_batch( + setup, + transcript, + statement, + std::slice::from_ref(&polynomial), + vec![hint], + ) } struct AkitaPackingSource<'a, S>(&'a S); @@ -298,39 +117,23 @@ struct PackingBatchShape<'a> { commitment: AkitaCommitment, } -fn validate_packed_adapter_prover_inputs( - setup: &::ProverSetup, +fn validate_packed_source_prover_inputs<'a, OpeningId, RelationId, S>( + setup: &'a AkitaPackingProverSetup, statement: &BatchOpeningStatement, - polynomials: &[Polynomial], - hints: &[AkitaProverHint], -) -> Result<(), OpeningsError> { + source: &S, + hint: &AkitaProverHint, +) -> Result, OpeningsError> +where + S: PackingWitnessSource, +{ let shape = validate_packed_adapter_statement(&setup.pcs, &setup.layout, statement)?; - if polynomials.len() != 1 { - return Err(invalid_batch(format!( - "Akita packing proof expects one packed witness polynomial, got {}", - polynomials.len() - ))); - } - if polynomials[0].num_vars() != shape.commitment.num_vars { - return Err(invalid_batch(format!( - "Akita packing witness polynomial has {} variables but commitment has {}", - polynomials[0].num_vars(), - shape.commitment.num_vars - ))); - } - if hints.len() != 1 || !hints[0].matches_commitment(&shape.commitment) { + validate_packing_source_layout(shape.layout, source)?; + if !hint.matches_commitment(&shape.commitment) { return Err(invalid_batch( "Akita packing proof requires one hint matching the packed witness commitment", )); } - Ok(()) -} - -fn validate_packed_adapter_verifier_inputs( - setup: &::VerifierSetup, - statement: &BatchOpeningStatement, -) -> Result<(), OpeningsError> { - validate_packed_adapter_statement(&setup.pcs, &setup.layout, statement).map(|_| ()) + Ok(shape) } fn validate_packed_adapter_statement<'a, Setup, OpeningId, RelationId>( @@ -351,25 +154,6 @@ where Ok(PackingBatchShape { layout, commitment }) } -fn validate_packed_source_prover_inputs<'a, OpeningId, RelationId, S>( - setup: &'a ::ProverSetup, - statement: &BatchOpeningStatement, - source: &S, - hint: &AkitaProverHint, -) -> Result, OpeningsError> -where - S: PackingWitnessSource, -{ - let shape = validate_packed_adapter_statement(&setup.pcs, &setup.layout, statement)?; - validate_packing_source_layout(shape.layout, source)?; - if !hint.matches_commitment(&shape.commitment) { - return Err(invalid_batch( - "Akita packing proof requires one hint matching the packed witness commitment", - )); - } - Ok(shape) -} - trait AkitaPackingSetupShape { fn max_num_vars(&self) -> usize; fn default_layout_digest(&self) -> [u8; 32]; @@ -385,16 +169,6 @@ impl AkitaPackingSetupShape for AkitaProverSetup { } } -impl AkitaPackingSetupShape for AkitaVerifierSetup { - fn max_num_vars(&self) -> usize { - self.max_num_vars - } - - fn default_layout_digest(&self) -> [u8; 32] { - self.default_layout_digest - } -} - fn validate_packed_setup_shape( max_num_vars: usize, default_layout_digest: [u8; 32], @@ -520,30 +294,6 @@ fn singleton_statement( } } -#[expect( - clippy::panic, - reason = "single-opening trait methods cannot return unsupported packed dense-path errors" -)] -fn unsupported_dense_packed_path(operation: &str) -> ! { - panic!( - "AkitaPackingScheme::{operation} cannot be used for dense proof-owned polynomials; use commit_packing_source/prove_packing_source_batch for W_pack or AkitaScheme direct openings for precommitted objects" - ) -} - -fn append_field_slice(transcript: &mut T, label: &'static [u8], values: &[AkitaField]) -where - T: Transcript, -{ - transcript.append(&jolt_transcript::LabelWithCount(label, values.len() as u64)); - for value in values { - value.append_to_transcript(transcript); - } -} - fn invalid_batch(reason: impl Into) -> OpeningsError { OpeningsError::InvalidBatch(reason.into()) } - -fn transparent_zk_error() -> OpeningsError { - invalid_batch("Akita packing batch openings do not support ZK mode yet") -} diff --git a/crates/jolt-verifier/src/akita_validation.rs b/crates/jolt-verifier/src/akita_validation.rs index aae193e543..dbcc67e4b3 100644 --- a/crates/jolt-verifier/src/akita_validation.rs +++ b/crates/jolt-verifier/src/akita_validation.rs @@ -412,33 +412,51 @@ mod tests { )] use super::*; + use crate::akita_packing::AkitaPackingScheme; use crate::{ akita::{commit_akita_packing_witness, AkitaPackingProverSetup}, - akita_packing::AkitaPackingScheme, proof::LatticeCommitmentPayload, stages::stage8::lattice_protocol_config_for_packed_witness_layout, }; + use jolt_claims::protocols::jolt::JoltPackingFamilyId; use jolt_openings::{ CommitmentScheme, PackingAlphabet, PackingCellAddress, PackingFactDomain, PackingFamilyId, PackingFamilySpec, PackingSetupParams, SparsePackingWitness, }; + fn physical(family: JoltPackingFamilyId) -> PackingFamilyId { + family.into() + } + fn tiny_layout() -> PackingWitnessLayout { - PackingWitnessLayout::new([ + let specs = vec![ PackingFamilySpec::direct( - PackingFamilyId::InstructionRa { index: 0 }, + physical(JoltPackingFamilyId::InstructionRa { index: 0 }), PackingFactDomain::TraceRows { log_t: 0 }, 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncMsb, + physical(JoltPackingFamilyId::UnsignedIncMsb), PackingFactDomain::TraceRows { log_t: 0 }, 1, PackingAlphabet::Bit, ), - ]) - .expect("layout should build") + ]; + #[cfg(feature = "field-inline")] + let specs = { + let mut specs = specs; + specs.extend((0..AkitaField::NUM_BYTES).map(|index| { + PackingFamilySpec::direct( + physical(JoltPackingFamilyId::FieldRdIncByte { index }), + PackingFactDomain::TraceRows { log_t: 0 }, + 1, + PackingAlphabet::Byte, + ) + })); + specs + }; + PackingWitnessLayout::new(specs).expect("layout should build") } fn packed_cell(family: PackingFamilyId, symbol: usize) -> PackingCellAddress { @@ -489,7 +507,8 @@ mod tests { fn akita_verifier_setup_binds_protocol_config() { let layout = tiny_layout(); let (_, verifier_setup) = akita_packing_setup(&layout, 1); - let config = lattice_protocol_config_for_packed_witness_layout(&layout); + let config = lattice_protocol_config_for_packed_witness_layout(&layout) + .expect("layout protocol config should derive"); validate_akita_verifier_setup_config(&verifier_setup, &config) .expect("setup should match generated Akita protocol config"); @@ -535,7 +554,7 @@ mod tests { .expect("setup should match generated Akita packing layout"); let other_layout = PackingWitnessLayout::new([PackingFamilySpec::direct( - PackingFamilyId::InstructionRa { index: 1 }, + physical(JoltPackingFamilyId::InstructionRa { index: 1 }), PackingFactDomain::TraceRows { log_t: 0 }, 1, PackingAlphabet::Byte, @@ -683,11 +702,11 @@ mod tests { layout.clone(), [ ( - packed_cell(PackingFamilyId::InstructionRa { index: 0 }, 7), + packed_cell(physical(JoltPackingFamilyId::InstructionRa { index: 0 }), 7), AkitaField::one(), ), ( - packed_cell(PackingFamilyId::UnsignedIncMsb, 1), + packed_cell(physical(JoltPackingFamilyId::UnsignedIncMsb), 1), AkitaField::one(), ), ], @@ -697,11 +716,11 @@ mod tests { layout.clone(), [ ( - packed_cell(PackingFamilyId::InstructionRa { index: 0 }, 8), + packed_cell(physical(JoltPackingFamilyId::InstructionRa { index: 0 }), 8), AkitaField::one(), ), ( - packed_cell(PackingFamilyId::UnsignedIncMsb, 0), + packed_cell(physical(JoltPackingFamilyId::UnsignedIncMsb), 0), AkitaField::one(), ), ], diff --git a/crates/jolt-verifier/src/akita_validity.rs b/crates/jolt-verifier/src/akita_validity.rs index 58ab9bdeb7..3508302f6c 100644 --- a/crates/jolt-verifier/src/akita_validity.rs +++ b/crates/jolt-verifier/src/akita_validity.rs @@ -1,10 +1,10 @@ +use crate::akita_packing::AkitaPackingScheme; use crate::{ akita::{ prove_akita_packing_openings, AkitaClearVectorCommitment, AkitaJoltProof, AkitaPackingBatchProof, AkitaPackingProverSetup, AkitaPackingWitnessArtifacts, AkitaVerifierPreprocessing, }, - akita_packing::AkitaPackingScheme, akita_validation::validate_akita_artifacts_for_proof, akita_validity_sumcheck::prove_combined_validity_sumcheck, proof::{ClearOnlyCommitment, JoltProofClaims}, @@ -24,6 +24,7 @@ use crate::{ }; use common::jolt_device::JoltDevice; use jolt_akita::{AkitaCommitment, AkitaField}; +use jolt_claims::protocols::jolt::JoltPackingFamilyId; use jolt_openings::{ PackingFamilyId, PackingValidityKind, PackingValidityRequirement, PackingWitnessLayout, PackingWitnessSource, @@ -258,7 +259,7 @@ fn validity_opening_value( where S: PackingWitnessSource, { - let family_id = statement.requirement.family.clone(); + let family_id = statement.requirement.family; let shape = validity_statement_shape(source.layout(), statement, &family_id)?; let point_parts = split_validity_point(statement.kind, point, shape)?; let row_weights = EqPolynomial::::evals(point_parts.row, None); @@ -470,10 +471,11 @@ where S: PackingWitnessSource, { let chunk = bytecode_store_rd_disjoint_chunk(&statement.requirement)?; - let store_id = PackingFamilyId::BytecodeCircuitFlag { + let store_id: PackingFamilyId = JoltPackingFamilyId::BytecodeCircuitFlag { chunk, flag: CircuitFlags::Store as usize, - }; + } + .into(); let store = source .layout() @@ -500,7 +502,8 @@ where match factor { 0 => weighted_direct_symbol_value(source, &store_id, &row_weights, 1), 1 => { - let rd_id = PackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }; + let rd_id: PackingFamilyId = + JoltPackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }.into(); let rd = source.layout().family(&rd_id).ok_or_else(|| { VerifierError::InvalidProtocolConfig { reason: format!("bytecode Store/Rd disjointness requires {rd_id:?}"), @@ -530,13 +533,15 @@ where fn bytecode_store_rd_disjoint_chunk( requirement: &PackingValidityRequirement, ) -> Result { - let PackingFamilyId::BytecodeCircuitFlag { chunk, flag } = &requirement.family else { + let Some(JoltPackingFamilyId::BytecodeCircuitFlag { chunk, flag }) = + JoltPackingFamilyId::from_physical_id(&requirement.family) + else { return Err(VerifierError::InvalidProtocolConfig { reason: "bytecode Store/Rd disjointness must be anchored on the Store circuit flag" .to_string(), }); }; - if *flag != CircuitFlags::Store as usize + if flag != CircuitFlags::Store as usize || requirement.limbs != 1 || requirement.alphabet_size != 2 { @@ -546,7 +551,7 @@ fn bytecode_store_rd_disjoint_chunk( .to_string(), }); } - Ok(*chunk) + Ok(chunk) } #[derive(Clone, Copy)] @@ -745,7 +750,6 @@ mod tests { use super::*; use crate::{ akita::{commit_akita_packing_witness_with_config, AkitaPackingVerifierSetup}, - akita_packing::AkitaPackingScheme, config::{IncrementCommitmentMode, JoltProtocolConfig, PcsFamily, ProgramMode}, proof::ClearOnlyCommitment, stages::{ @@ -764,15 +768,20 @@ mod tests { dimensions::{TracePolynomialOrder, REGISTER_ADDRESS_BITS}, ra::JoltRaPolynomialLayout, }, + JoltAdviceKind, }; use jolt_field::FixedByteSize; use jolt_openings::{ - packing_validity_digest, CommitmentScheme, PackingAdviceKind, PackingAlphabet, - PackingCellAddress, PackingFactDomain, PackingFamilyId, PackingFamilySpec, - PackingSetupParams, PackingValidityKind, PackingValidityRequirement, SparsePackingWitness, + packing_validity_digest, CommitmentScheme, PackingAlphabet, PackingCellAddress, + PackingFactDomain, PackingFamilyId, PackingFamilySpec, PackingSetupParams, + PackingValidityKind, PackingValidityRequirement, SparsePackingWitness, }; use jolt_transcript::{Blake2bTranscript, Transcript}; + fn physical(family: JoltPackingFamilyId) -> PackingFamilyId { + family.into() + } + fn run_on_large_stack(test: impl FnOnce() + Send + 'static) { std::thread::Builder::new() .stack_size(256 * 1024 * 1024) @@ -987,7 +996,8 @@ mod tests { &requirements, &jolt_akita::AKITA_FIELD_MODULUS.to_le_bytes(), ); - let mut config = lattice_protocol_config_for_packed_witness_layout(&layout); + let mut config = lattice_protocol_config_for_packed_witness_layout(&layout) + .expect("layout protocol config should derive"); config.lattice.packed_witness.validity_digest = Some(packing_validity_digest(&requirements)); let params = akita_packing_params(&layout, 1); @@ -1051,11 +1061,10 @@ mod tests { let statement = statements .iter() .find(|statement| { - matches!( - statement.requirement.family, - PackingFamilyId::FieldRdIncByte { index: 0 } - ) && statement.kind - == LatticePackedValidityStatementKind::FieldElementCanonicalBytes + statement.requirement.family + == physical(JoltPackingFamilyId::FieldRdIncByte { index: 0 }) + && statement.kind + == LatticePackedValidityStatementKind::FieldElementCanonicalBytes }) .expect("FieldRdInc canonical-byte statement should exist"); let point = vec![AkitaField::zero(); statement.num_vars]; @@ -1076,11 +1085,10 @@ mod tests { let statement = statements .iter() .find(|statement| { - matches!( - statement.requirement.family, - PackingFamilyId::BytecodeImmBytes { chunk: 0 } - ) && statement.kind - == LatticePackedValidityStatementKind::FieldElementCanonicalBytes + statement.requirement.family + == physical(JoltPackingFamilyId::BytecodeImmBytes { chunk: 0 }) + && statement.kind + == LatticePackedValidityStatementKind::FieldElementCanonicalBytes }) .expect("bytecode immediate canonical-byte statement should exist"); let point = vec![AkitaField::zero(); statement.num_vars]; @@ -1093,24 +1101,24 @@ mod tests { #[test] fn packed_validity_value_detects_malformed_advice_byte_onehot() { let (layout, statements) = small_validity_context(); - let family = PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Untrusted, + let family = physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Untrusted, index: 0, - }; + }); let source = SparsePackingWitness::try_from_cells( layout, [ - (packed_cell_at(family.clone(), 0, 0, 7), AkitaField::one()), + (packed_cell_at(family, 0, 0, 7), AkitaField::one()), (packed_cell_at(family, 0, 0, 8), AkitaField::one()), ], ) .expect("malformed advice source should build"); let statement = validity_statement( &statements, - PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Untrusted, + physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Untrusted, index: 0, - }, + }), LatticePackedValidityStatementKind::ExactOneHotRowSum, ); @@ -1123,24 +1131,24 @@ mod tests { #[test] fn packed_validity_value_detects_malformed_bytecode_optional_selector() { let (layout, statements, _) = small_bytecode_validity_context(); - let family = PackingFamilyId::BytecodeRegisterSelector { + let family = physical(JoltPackingFamilyId::BytecodeRegisterSelector { chunk: 0, selector: 0, - }; + }); let source = SparsePackingWitness::try_from_cells( layout, [ - (packed_cell_at(family.clone(), 0, 0, 3), AkitaField::one()), + (packed_cell_at(family, 0, 0, 3), AkitaField::one()), (packed_cell_at(family, 0, 0, 4), AkitaField::one()), ], ) .expect("malformed bytecode selector source should build"); let statement = validity_statement( &statements, - PackingFamilyId::BytecodeRegisterSelector { + physical(JoltPackingFamilyId::BytecodeRegisterSelector { chunk: 0, selector: 0, - }, + }), LatticePackedValidityStatementKind::OptionalOneHotRowSum, ); @@ -1154,7 +1162,7 @@ mod tests { fn packed_validity_value_detects_malformed_bytecode_boolean_flag() { let (layout, statements, _) = small_bytecode_validity_context(); let flag = CircuitFlags::Store as usize; - let family = PackingFamilyId::BytecodeCircuitFlag { chunk: 0, flag }; + let family = physical(JoltPackingFamilyId::BytecodeCircuitFlag { chunk: 0, flag }); let source = SparsePackingWitness::try_from_cells( layout, [(packed_cell_at(family, 0, 0, 1), af(2))], @@ -1162,7 +1170,7 @@ mod tests { .expect("malformed bytecode flag source should build"); let statement = validity_statement( &statements, - PackingFamilyId::BytecodeCircuitFlag { chunk: 0, flag }, + physical(JoltPackingFamilyId::BytecodeCircuitFlag { chunk: 0, flag }), LatticePackedValidityStatementKind::BooleanIndicator, ); @@ -1224,10 +1232,10 @@ mod tests { ) { let specs = vec![ PackingFamilySpec::direct( - PackingFamilyId::BytecodeRegisterSelector { + physical(JoltPackingFamilyId::BytecodeRegisterSelector { chunk: 0, selector: 0, - }, + }), PackingFactDomain::BytecodeRows { log_bytecode: 0 }, 1, PackingAlphabet::Fixed { @@ -1235,10 +1243,10 @@ mod tests { }, ), PackingFamilySpec::direct( - PackingFamilyId::BytecodeRegisterSelector { + physical(JoltPackingFamilyId::BytecodeRegisterSelector { chunk: 0, selector: 2, - }, + }), PackingFactDomain::BytecodeRows { log_bytecode: 0 }, 1, PackingAlphabet::Fixed { @@ -1246,16 +1254,16 @@ mod tests { }, ), PackingFamilySpec::direct( - PackingFamilyId::BytecodeCircuitFlag { + physical(JoltPackingFamilyId::BytecodeCircuitFlag { chunk: 0, flag: CircuitFlags::Store as usize, - }, + }), PackingFactDomain::BytecodeRows { log_bytecode: 0 }, 1, PackingAlphabet::Bit, ), PackingFamilySpec::direct( - PackingFamilyId::BytecodeImmBytes { chunk: 0 }, + physical(JoltPackingFamilyId::BytecodeImmBytes { chunk: 0 }), PackingFactDomain::BytecodeRows { log_bytecode: 0 }, AkitaField::NUM_BYTES, PackingAlphabet::Byte, @@ -1266,7 +1274,7 @@ mod tests { let mut specs = specs; specs.extend((0..AkitaField::NUM_BYTES).map(|index| { PackingFamilySpec::direct( - PackingFamilyId::FieldRdIncByte { index }, + physical(JoltPackingFamilyId::FieldRdIncByte { index }), PackingFactDomain::TraceRows { log_t: 0 }, 1, PackingAlphabet::Byte, @@ -1276,7 +1284,8 @@ mod tests { }; let layout = PackingWitnessLayout::new(specs).expect("manual bytecode validity layout should build"); - let mut requirements = lattice_validity_requirements_for_packed_witness_layout(&layout); + let mut requirements = lattice_validity_requirements_for_packed_witness_layout(&layout) + .expect("layout validity requirements should derive"); requirements.push(bytecode_imm_canonical_bytes_requirement( 0, AkitaField::NUM_BYTES, @@ -1337,9 +1346,11 @@ mod tests { requirements: &[PackingValidityRequirement], bytes: &[u8], ) -> SparsePackingWitness { - validity_source_with_symbols(layout, requirements, |family, _| match family { - PackingFamilyId::FieldRdIncByte { index } => bytes[*index] as usize, - _ => 0, + validity_source_with_symbols(layout, requirements, |family, _| { + match JoltPackingFamilyId::from_physical_id(family) { + Some(JoltPackingFamilyId::FieldRdIncByte { index }) => bytes[index] as usize, + _ => 0, + } }) } @@ -1348,9 +1359,11 @@ mod tests { requirements: &[PackingValidityRequirement], bytes: &[u8], ) -> SparsePackingWitness { - validity_source_with_symbols(layout, requirements, |family, limb| match family { - PackingFamilyId::BytecodeImmBytes { .. } => bytes[limb] as usize, - _ => 0, + validity_source_with_symbols(layout, requirements, |family, limb| { + match JoltPackingFamilyId::from_physical_id(family) { + Some(JoltPackingFamilyId::BytecodeImmBytes { .. }) => bytes[limb] as usize, + _ => 0, + } }) } @@ -1361,7 +1374,7 @@ mod tests { ) -> SparsePackingWitness { let mut cells = Vec::new(); for requirement in requirements { - let family_id = requirement.family.clone(); + let family_id = requirement.family; let family = layout .family(&family_id) .expect("validity family should exist"); @@ -1374,7 +1387,7 @@ mod tests { let symbol = symbol_for(&requirement.family, limb); cells.push(( PackingCellAddress { - family: family_id.clone(), + family: family_id, row, limb, symbol, diff --git a/crates/jolt-verifier/src/akita_witness.rs b/crates/jolt-verifier/src/akita_witness.rs index 6b357c4cf1..0e2560fa23 100644 --- a/crates/jolt-verifier/src/akita_witness.rs +++ b/crates/jolt-verifier/src/akita_witness.rs @@ -1,8 +1,11 @@ use jolt_akita::AkitaField; -use jolt_claims::protocols::jolt::unsigned_inc_lower_chunk_count; +use jolt_claims::protocols::jolt::{ + packed_family_is_precommitted, unsigned_inc_lower_chunk_count, JoltAdviceKind, + JoltPackingFamilyId, +}; use jolt_field::{CanonicalBytes, FromPrimitiveInt}; use jolt_openings::{ - PackingAdviceKind, PackingCellAddress, PackingFactDomain, PackingFamilyId, PackingLayoutError, + PackingCellAddress, PackingFactDomain, PackingFamilyId, PackingLayoutError, PackingWitnessLayout, SparsePackingWitness, }; use jolt_riscv::JoltTraceRow; @@ -63,19 +66,20 @@ impl JoltPackedWitnessBuilder { }); } let instruction_chunks = self.max_trace_family_index(|family| { - matches!(family, PackingFamilyId::InstructionRa { .. }) + matches!(family, JoltPackingFamilyId::InstructionRa { .. }) + }); + let bytecode_chunks = self.max_trace_family_index(|family| { + matches!(family, JoltPackingFamilyId::BytecodeRa { .. }) }); - let bytecode_chunks = self - .max_trace_family_index(|family| matches!(family, PackingFamilyId::BytecodeRa { .. })); - let ram_chunks = - self.max_trace_family_index(|family| matches!(family, PackingFamilyId::RamRa { .. })); + let ram_chunks = self + .max_trace_family_index(|family| matches!(family, JoltPackingFamilyId::RamRa { .. })); for (row_index, row) in rows.iter().enumerate() { let lookup_index = lookup_index(row_index, row); for index in 0..instruction_chunks { let symbol = chunk(lookup_index, index, instruction_chunks, log_k_chunk)?; self.emit_one( - PackingFamilyId::InstructionRa { index }, + JoltPackingFamilyId::InstructionRa { index }.into(), row_index, 0, symbol, @@ -85,13 +89,23 @@ impl JoltPackedWitnessBuilder { let pc = row.pc() as u128; for index in 0..bytecode_chunks { let symbol = chunk(pc, index, bytecode_chunks, log_k_chunk)?; - self.emit_one(PackingFamilyId::BytecodeRa { index }, row_index, 0, symbol)?; + self.emit_one( + JoltPackingFamilyId::BytecodeRa { index }.into(), + row_index, + 0, + symbol, + )?; } if let Some(address) = ram_address(row_index, row) { for index in 0..ram_chunks { let symbol = chunk(address as u128, index, ram_chunks, log_k_chunk)?; - self.emit_one(PackingFamilyId::RamRa { index }, row_index, 0, symbol)?; + self.emit_one( + JoltPackingFamilyId::RamRa { index }.into(), + row_index, + 0, + symbol, + )?; } } @@ -104,10 +118,9 @@ impl JoltPackedWitnessBuilder { &mut self, bytes: &[u8], ) -> Result<&mut Self, JoltPackedWitnessError> { - let kind = PackingAdviceKind::Untrusted; let domain = "untrusted advice bytes"; let expected = self - .advice_byte_count(kind)? + .untrusted_advice_byte_count()? .ok_or(JoltPackedWitnessError::MissingDomain { domain })?; if bytes.len() != expected { return Err(JoltPackedWitnessError::LengthMismatch { @@ -118,7 +131,11 @@ impl JoltPackedWitnessBuilder { } for (row, byte) in bytes.iter().copied().enumerate() { self.emit_byte( - PackingFamilyId::AdviceBytes { kind, index: 0 }, + JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Untrusted, + index: 0, + } + .into(), row, 0, byte, @@ -154,7 +171,7 @@ impl JoltPackedWitnessBuilder { if self .layout - .family(&PackingFamilyId::FieldRdIncByte { index: 0 }) + .family(&JoltPackingFamilyId::FieldRdIncByte { index: 0 }.into()) .is_some() { let rd_delta = if row.rd_index().is_some() { @@ -165,7 +182,7 @@ impl JoltPackedWitnessBuilder { let encoded = AkitaField::from_i128(rd_delta).to_bytes_le_vec(); for (index, byte) in encoded.into_iter().enumerate() { self.emit_byte( - PackingFamilyId::FieldRdIncByte { index }, + JoltPackingFamilyId::FieldRdIncByte { index }.into(), row_index, 0, byte, @@ -196,14 +213,14 @@ impl JoltPackedWitnessBuilder { let msb = shifted >> 64; for index in 0..chunk_count { self.emit_one( - PackingFamilyId::UnsignedIncChunk { index }, + JoltPackingFamilyId::UnsignedIncChunk { index }.into(), row, 0, little_endian_chunk(lower, index, log_k_chunk)?, )?; } if msb == 1 { - self.emit_one(PackingFamilyId::UnsignedIncMsb, row, 0, 1)?; + self.emit_one(JoltPackingFamilyId::UnsignedIncMsb.into(), row, 0, 1)?; } Ok(()) } @@ -248,29 +265,33 @@ impl JoltPackedWitnessBuilder { Ok(rows) } - fn advice_byte_count( - &self, - kind: PackingAdviceKind, - ) -> Result, JoltPackedWitnessError> { + fn untrusted_advice_byte_count(&self) -> Result, JoltPackedWitnessError> { let mut rows = None; for family in &self.layout.families { - if family.id == (PackingFamilyId::AdviceBytes { kind, index: 0 }) { + if family.id + == (JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Untrusted, + index: 0, + } + .into()) + { merge_domain_rows(&mut rows, family.domain, "untrusted advice bytes")?; } } Ok(rows) } - fn max_trace_family_index(&self, is_family: impl Fn(&PackingFamilyId) -> bool) -> usize { + fn max_trace_family_index(&self, is_family: impl Fn(JoltPackingFamilyId) -> bool) -> usize { self.layout .families .iter() .filter(|family| matches!(family.domain, PackingFactDomain::TraceRows { .. })) - .filter(|family| is_family(&family.id)) - .filter_map(|family| match family.id { - PackingFamilyId::InstructionRa { index } - | PackingFamilyId::BytecodeRa { index } - | PackingFamilyId::RamRa { index } => Some(index + 1), + .filter_map(|family| JoltPackingFamilyId::from_physical_id(&family.id)) + .filter(|family| is_family(*family)) + .filter_map(|family| match family { + JoltPackingFamilyId::InstructionRa { index } + | JoltPackingFamilyId::BytecodeRa { index } + | JoltPackingFamilyId::RamRa { index } => Some(index + 1), _ => None, }) .max() @@ -282,7 +303,7 @@ pub fn build_akita_packing_jolt_witness( input: AkitaPackingJoltWitnessInput<'_>, ) -> Result, VerifierError> { validate_akita_jolt_packed_witness_layout(&input.layout)?; - let protocol = lattice_protocol_config_for_packed_witness_layout(&input.layout); + let protocol = lattice_protocol_config_for_packed_witness_layout(&input.layout)?; validate_lattice_packed_witness_layout_config(&protocol, &input.layout)?; if input.instruction_lookup_indices.len() != input.trace_rows.len() { @@ -313,7 +334,7 @@ fn validate_akita_jolt_packed_witness_layout( layout: &PackingWitnessLayout, ) -> Result<(), VerifierError> { for family in &layout.families { - if jolt_packed_witness_family_is_precommitted(&family.id) { + if packed_family_is_precommitted(&family.id) { return Err(VerifierError::InvalidProtocolConfig { reason: format!( "precommitted family {:?} cannot be included in the lattice packing witness layout", @@ -325,24 +346,6 @@ fn validate_akita_jolt_packed_witness_layout( Ok(()) } -fn jolt_packed_witness_family_is_precommitted(family: &PackingFamilyId) -> bool { - matches!( - family, - PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Trusted, - .. - } | PackingFamilyId::BytecodeChunk { .. } - | PackingFamilyId::BytecodeRegisterSelector { .. } - | PackingFamilyId::BytecodeCircuitFlag { .. } - | PackingFamilyId::BytecodeInstructionFlag { .. } - | PackingFamilyId::BytecodeLookupSelector { .. } - | PackingFamilyId::BytecodeRafFlag { .. } - | PackingFamilyId::BytecodeUnexpandedPcBytes { .. } - | PackingFamilyId::BytecodeImmBytes { .. } - | PackingFamilyId::ProgramImageInit - ) -} - fn pack_untrusted_advice_bytes( builder: &mut JoltPackedWitnessBuilder, bytes: Option<&[u8]>, @@ -351,11 +354,11 @@ fn pack_untrusted_advice_bytes( builder.layout(), |id| { matches!( - id, - PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Untrusted, + JoltPackingFamilyId::from_physical_id(id), + Some(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Untrusted, index: 0, - } + }) ) }, "untrusted advice bytes", @@ -539,12 +542,18 @@ mod tests { use super::*; use jolt_field::FixedByteSize; - use jolt_openings::{PackingAlphabet, PackingFamilySpec, PackingWitnessSource}; + use jolt_openings::{ + PackingAdviceKind, PackingAlphabet, PackingFamilySpec, PackingWitnessSource, + }; use jolt_riscv::{ CapturedState, JoltInstructionKind, JoltInstructionRow, LoadState, NormalizedOperands, StoreState, }; + fn physical(family: JoltPackingFamilyId) -> PackingFamilyId { + family.into() + } + fn trace_domain() -> PackingFactDomain { PackingFactDomain::TraceRows { log_t: 1 } } @@ -588,31 +597,31 @@ mod tests { fn packs_trace_ra_and_unsigned_increment_facts() { let layout = PackingWitnessLayout::new([ PackingFamilySpec::direct( - PackingFamilyId::InstructionRa { index: 0 }, + physical(JoltPackingFamilyId::InstructionRa { index: 0 }), trace_domain(), 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::BytecodeRa { index: 0 }, + physical(JoltPackingFamilyId::BytecodeRa { index: 0 }), trace_domain(), 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::RamRa { index: 0 }, + physical(JoltPackingFamilyId::RamRa { index: 0 }), trace_domain(), 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncChunk { index: 0 }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 0 }), trace_domain(), 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncMsb, + physical(JoltPackingFamilyId::UnsignedIncMsb), trace_domain(), 1, PackingAlphabet::Bit, @@ -668,7 +677,7 @@ mod tests { assert_eq!( get( &witness, - PackingFamilyId::InstructionRa { index: 0 }, + physical(JoltPackingFamilyId::InstructionRa { index: 0 }), 0, 0, 0x7f @@ -676,28 +685,47 @@ mod tests { AkitaField::one() ); assert_eq!( - get(&witness, PackingFamilyId::BytecodeRa { index: 0 }, 1, 0, 11), + get( + &witness, + physical(JoltPackingFamilyId::BytecodeRa { index: 0 }), + 1, + 0, + 11 + ), AkitaField::one() ); assert_eq!( - get(&witness, PackingFamilyId::RamRa { index: 0 }, 1, 0, 0x42), + get( + &witness, + physical(JoltPackingFamilyId::RamRa { index: 0 }), + 1, + 0, + 0x42 + ), AkitaField::one() ); assert_eq!( get( &witness, - PackingFamilyId::UnsignedIncChunk { index: 0 }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 0 }), 0, 0, 249 ), AkitaField::one() ); - assert!(get(&witness, PackingFamilyId::UnsignedIncMsb, 0, 0, 1).is_zero()); + assert!(get( + &witness, + physical(JoltPackingFamilyId::UnsignedIncMsb), + 0, + 0, + 1 + ) + .is_zero()); assert_eq!( get( &witness, - PackingFamilyId::UnsignedIncChunk { index: 0 }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 0 }), 1, 0, 20 @@ -705,7 +733,13 @@ mod tests { AkitaField::one() ); assert_eq!( - get(&witness, PackingFamilyId::UnsignedIncMsb, 1, 0, 1), + get( + &witness, + physical(JoltPackingFamilyId::UnsignedIncMsb), + 1, + 0, + 1 + ), AkitaField::one() ); } @@ -758,7 +792,7 @@ mod tests { assert_eq!( get( &witness, - PackingFamilyId::UnsignedIncChunk { index }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index }), 0, 0, 0 @@ -767,7 +801,13 @@ mod tests { ); } assert_eq!( - get(&witness, PackingFamilyId::UnsignedIncMsb, 0, 0, 1), + get( + &witness, + physical(JoltPackingFamilyId::UnsignedIncMsb), + 0, + 0, + 1 + ), AkitaField::one() ); } @@ -805,7 +845,7 @@ mod tests { assert_eq!( get( &witness, - PackingFamilyId::UnsignedIncChunk { index }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index }), 0, 0, 0 @@ -814,7 +854,13 @@ mod tests { ); } assert_eq!( - get(&witness, PackingFamilyId::UnsignedIncMsb, 0, 0, 1), + get( + &witness, + physical(JoltPackingFamilyId::UnsignedIncMsb), + 0, + 0, + 1 + ), AkitaField::one() ); } @@ -851,7 +897,7 @@ mod tests { assert_eq!( get( &witness, - PackingFamilyId::UnsignedIncChunk { index: 0 }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 0 }), 0, 0, 3 @@ -862,7 +908,7 @@ mod tests { assert_eq!( get( &witness, - PackingFamilyId::UnsignedIncChunk { index }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index }), 0, 0, 0 @@ -871,7 +917,13 @@ mod tests { ); } assert_eq!( - get(&witness, PackingFamilyId::UnsignedIncMsb, 0, 0, 1), + get( + &witness, + physical(JoltPackingFamilyId::UnsignedIncMsb), + 0, + 0, + 1 + ), AkitaField::one() ); } @@ -909,7 +961,7 @@ mod tests { assert_eq!( get( &witness, - PackingFamilyId::UnsignedIncChunk { index }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index }), 0, 0, 0 @@ -918,7 +970,13 @@ mod tests { ); } assert_eq!( - get(&witness, PackingFamilyId::UnsignedIncMsb, 0, 0, 1), + get( + &witness, + physical(JoltPackingFamilyId::UnsignedIncMsb), + 0, + 0, + 1 + ), AkitaField::one() ); } @@ -1004,7 +1062,7 @@ mod tests { assert_eq!( get( &witness, - PackingFamilyId::UnsignedIncChunk { index: 0 }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 0 }), 0, 0, 249 @@ -1015,7 +1073,7 @@ mod tests { assert_eq!( get( &witness, - PackingFamilyId::UnsignedIncChunk { index }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index }), 0, 0, 255 @@ -1023,7 +1081,14 @@ mod tests { AkitaField::one() ); } - assert!(get(&witness, PackingFamilyId::UnsignedIncMsb, 0, 0, 1).is_zero()); + assert!(get( + &witness, + physical(JoltPackingFamilyId::UnsignedIncMsb), + 0, + 0, + 1 + ) + .is_zero()); } #[test] @@ -1060,7 +1125,7 @@ mod tests { assert_eq!( get( &witness, - PackingFamilyId::UnsignedIncChunk { index }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index }), 0, 0, symbol @@ -1072,7 +1137,7 @@ mod tests { assert_eq!( get( &witness, - PackingFamilyId::UnsignedIncChunk { index }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index }), 0, 0, 0 @@ -1081,7 +1146,13 @@ mod tests { ); } assert_eq!( - get(&witness, PackingFamilyId::UnsignedIncMsb, 0, 0, 1), + get( + &witness, + physical(JoltPackingFamilyId::UnsignedIncMsb), + 0, + 0, + 1 + ), AkitaField::one() ); } @@ -1136,7 +1207,7 @@ mod tests { assert_eq!( get( &witness, - PackingFamilyId::FieldRdIncByte { index }, + physical(JoltPackingFamilyId::FieldRdIncByte { index }), 0, 0, byte as usize @@ -1146,7 +1217,7 @@ mod tests { } assert!(witness .layout() - .family(&PackingFamilyId::FieldRdIncSign) + .family(&physical(JoltPackingFamilyId::FieldRdIncSign)) .is_none()); } @@ -1181,7 +1252,13 @@ mod tests { for index in 0..AkitaField::NUM_BYTES { assert_eq!( - get(&witness, PackingFamilyId::FieldRdIncByte { index }, 0, 0, 0), + get( + &witness, + physical(JoltPackingFamilyId::FieldRdIncByte { index }), + 0, + 0, + 0 + ), AkitaField::one() ); } @@ -1202,10 +1279,10 @@ mod tests { assert_eq!( get( &witness, - PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Untrusted, + physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Untrusted, index: 0, - }, + }), row, 0, byte as usize @@ -1216,8 +1293,15 @@ mod tests { } fn advice_layout(kind: PackingAdviceKind) -> PackingWitnessLayout { + let jolt_kind = match kind { + PackingAdviceKind::Trusted => JoltAdviceKind::Trusted, + PackingAdviceKind::Untrusted => JoltAdviceKind::Untrusted, + }; PackingWitnessLayout::new([PackingFamilySpec::direct( - PackingFamilyId::AdviceBytes { kind, index: 0 }, + physical(JoltPackingFamilyId::AdviceBytes { + kind: jolt_kind, + index: 0, + }), PackingFactDomain::AdviceBytes { kind, log_bytes: 2 }, 1, PackingAlphabet::Byte, @@ -1228,7 +1312,7 @@ mod tests { fn field_rd_inc_layout() -> PackingWitnessLayout { PackingWitnessLayout::new((0..AkitaField::NUM_BYTES).map(|index| { PackingFamilySpec::direct( - PackingFamilyId::FieldRdIncByte { index }, + physical(JoltPackingFamilyId::FieldRdIncByte { index }), trace_domain(), 1, PackingAlphabet::Byte, @@ -1250,7 +1334,7 @@ mod tests { let mut specs = (0..chunk_count) .map(|index| { PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncChunk { index }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index }), trace_domain(), 1, alphabet, @@ -1258,7 +1342,7 @@ mod tests { }) .collect::>(); specs.push(PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncMsb, + physical(JoltPackingFamilyId::UnsignedIncMsb), trace_domain(), 1, PackingAlphabet::Bit, diff --git a/crates/jolt-verifier/src/compat/audit.rs b/crates/jolt-verifier/src/compat/audit.rs index 5864dbc515..a73c76baf6 100644 --- a/crates/jolt-verifier/src/compat/audit.rs +++ b/crates/jolt-verifier/src/compat/audit.rs @@ -4,7 +4,9 @@ use common::jolt_device::JoltDevice; use jolt_blindfold::BlindFoldProtocol; use jolt_crypto::{HomomorphicCommitment, VectorCommitment}; use jolt_field::Field; -use jolt_openings::{BatchOpeningScheme, CommitmentScheme, ZkBatchOpeningScheme}; +use jolt_openings::{ + BatchOpeningScheme, CommitmentLayoutDigest, CommitmentScheme, ZkBatchOpeningScheme, +}; use jolt_transcript::{AppendToTranscript, Transcript}; use crate::{ @@ -60,7 +62,9 @@ where PCS: CommitmentScheme + BatchOpeningScheme + ZkBatchOpeningScheme, - PCS::Output: AppendToTranscript, + // Stage 8 still uses shared clear/ZK statement construction; splitting a + // ZK-only builder would be broader than this audit helper. + PCS::Output: AppendToTranscript + CommitmentLayoutDigest, VC: VectorCommitment, VC::Output: Copy + HomomorphicCommitment + AppendToTranscript, T: Transcript, diff --git a/crates/jolt-verifier/src/lib.rs b/crates/jolt-verifier/src/lib.rs index 2aa47a5e55..8fb5a0f1da 100644 --- a/crates/jolt-verifier/src/lib.rs +++ b/crates/jolt-verifier/src/lib.rs @@ -1,7 +1,7 @@ //! Verifier model crate for Jolt proofs. #[cfg(feature = "akita")] -pub mod akita; +mod akita; #[cfg(feature = "akita")] mod akita_openings; #[cfg(feature = "akita")] @@ -25,16 +25,24 @@ mod verifier; #[cfg(feature = "akita")] pub use akita::{ - attach_akita_packing_validity_proof, commit_akita_packing_witness, - commit_akita_packing_witness_with_config, prove_akita_jolt_final_openings, - prove_akita_jolt_final_openings_with_precommitted, prove_akita_jolt_packed_validity, - prove_akita_packing_openings, prove_akita_packing_validity, prove_akita_stage8_clear_openings, - prove_akita_stage8_clear_openings_with_precommitted, prove_and_attach_akita_opening_proofs, - prove_and_attach_akita_opening_proofs_with_precommitted, verify_akita_clear, - AkitaClearVectorCommitment, AkitaJoltProof, AkitaPackingValidityProofArtifacts, - AkitaPackingWitnessArtifacts, AkitaPrecommittedOpeningInput, AkitaStage8ClearOpeningProofs, - AkitaVerifierPreprocessing, + verify_akita_clear, AkitaClearVectorCommitment, AkitaJoltProof, AkitaVerifierPreprocessing, }; +#[cfg(feature = "akita")] +pub mod prover_support { + pub use crate::akita::{ + attach_akita_packing_validity_proof, build_akita_packing_jolt_witness, + commit_akita_packing_jolt_witness, commit_akita_packing_witness, + commit_akita_packing_witness_with_config, prove_akita_jolt_final_openings, + prove_akita_jolt_final_openings_with_precommitted, prove_akita_jolt_packed_validity, + prove_akita_packing_openings, prove_akita_packing_validity, + prove_akita_stage8_clear_openings, prove_akita_stage8_clear_openings_with_precommitted, + prove_and_attach_akita_opening_proofs, + prove_and_attach_akita_opening_proofs_with_precommitted, AkitaCommittedPackedJoltWitness, + AkitaPackingBatchProof, AkitaPackingJoltWitnessInput, AkitaPackingProverSetup, + AkitaPackingValidityProofArtifacts, AkitaPackingVerifierSetup, + AkitaPackingWitnessArtifacts, AkitaPrecommittedOpeningInput, AkitaStage8ClearOpeningProofs, + }; +} pub use config::{ validate_proof_config, validate_protocol_config, AdviceLatticeConfig, FieldInlineLatticeConfig, IncrementCommitmentMode, JoltProtocolConfig, LatticeConfig, PackedWitnessConfig, PcsFamily, diff --git a/crates/jolt-verifier/src/stages/stage8/lattice.rs b/crates/jolt-verifier/src/stages/stage8/lattice.rs index 0b520636b7..ab01374682 100644 --- a/crates/jolt-verifier/src/stages/stage8/lattice.rs +++ b/crates/jolt-verifier/src/stages/stage8/lattice.rs @@ -9,14 +9,14 @@ use jolt_claims::protocols::field_inline::{ use jolt_claims::protocols::jolt::{ byte_decode_terms, unsigned_inc_msb_lattice_view_formula, unsigned_inc_msb_opening, weighted_symbol_terms, JoltAdviceKind, JoltCommittedPolynomial, JoltOpeningId, - JoltPolynomialId, JoltRelationId, + JoltPackingFamilyId, JoltPolynomialId, JoltRelationId, }; use jolt_field::Field; #[cfg(feature = "field-inline")] use jolt_field::FixedByteSize; use jolt_openings::{ - PackingAdviceKind, PackingFamilyId, PackingValidityRequirement, PackingViewError, - PackingViewFormula, PackingWitnessLayout, + PackingFamilyId, PackingValidityRequirement, PackingViewError, PackingViewFormula, + PackingWitnessLayout, }; use jolt_poly::EqPolynomial; @@ -292,7 +292,7 @@ where JoltCommittedPolynomial::InstructionRa(index), JoltRelationId::HammingWeightClaimReduction, ) => ra_lattice_view_formula( - PackingFamilyId::InstructionRa { index }, + JoltPackingFamilyId::InstructionRa { index }.into(), point, log_k_chunk, ), @@ -301,14 +301,14 @@ where JoltRelationId::HammingWeightClaimReduction, ) => { ra_lattice_view_formula( - PackingFamilyId::BytecodeRa { index }, + JoltPackingFamilyId::BytecodeRa { index }.into(), point, log_k_chunk, ) } (JoltCommittedPolynomial::RamRa(index), JoltRelationId::HammingWeightClaimReduction) => { ra_lattice_view_formula( - PackingFamilyId::RamRa { index }, + JoltPackingFamilyId::RamRa { index }.into(), point, log_k_chunk, ) @@ -373,7 +373,7 @@ where ))); } ra_lattice_view_formula( - PackingFamilyId::UnsignedIncChunk { index }, + JoltPackingFamilyId::UnsignedIncChunk { index }.into(), point, log_k_chunk, ) @@ -410,21 +410,11 @@ where F: Field, { PackingViewFormula::linear_decoded(byte_decode_terms( - PackingFamilyId::AdviceBytes { - kind: packing_advice_kind(kind), - index: 0, - }, + JoltPackingFamilyId::AdviceBytes { kind, index: 0 }.into(), 0, )) } -fn packing_advice_kind(kind: JoltAdviceKind) -> PackingAdviceKind { - match kind { - JoltAdviceKind::Trusted => PackingAdviceKind::Trusted, - JoltAdviceKind::Untrusted => PackingAdviceKind::Untrusted, - } -} - fn power_of_two_log(value: usize, name: &'static str) -> Result { if value.is_power_of_two() { Ok(value.trailing_zeros() as usize) diff --git a/crates/jolt-verifier/src/stages/stage8/lattice_layout.rs b/crates/jolt-verifier/src/stages/stage8/lattice_layout.rs index 02a970d7d8..882f482296 100644 --- a/crates/jolt-verifier/src/stages/stage8/lattice_layout.rs +++ b/crates/jolt-verifier/src/stages/stage8/lattice_layout.rs @@ -13,16 +13,17 @@ use jolt_akita::AKITA_FIELD_MODULUS; #[cfg(feature = "field-inline")] use jolt_claims::protocols::field_inline::formulas::lattice as field_lattice; use jolt_claims::protocols::jolt::{ - advice_bytes_validity_requirement, formulas::ra::JoltRaPolynomialLayout, - unsigned_inc_validity_requirements, AdviceClaimReductionLayout, JoltAdviceKind, + derive_jolt_lattice_packed_validity_requirements, derive_jolt_lattice_packed_witness_layout, + formulas::ra::JoltRaPolynomialLayout, + lattice_validity_requirements_for_packed_witness_layout as claims_lattice_validity_requirements_for_packed_witness_layout, + layout_has_advice, packed_family_is_precommitted, AdviceClaimReductionLayout, + FieldRdIncPacking, JoltAdviceKind, JoltLatticeLayoutError, JoltLatticePackingInputs, + JoltLatticeValidityInputs, JoltPackingFamilyId, }; use jolt_field::FixedByteSize; use jolt_openings::{ - packing_validity_digest, PackingAdviceKind, PackingAlphabet, PackingFactDomain, - PackingFamilyId, PackingFamilySpec, PackingValidityKind, PackingValidityRequirement, - PackingWitnessLayout, + packing_validity_digest, PackingAdviceKind, PackingValidityRequirement, PackingWitnessLayout, }; -use jolt_riscv::CircuitFlags; use super::{invalid_lattice_config, invalid_precommitted_schedule}; @@ -39,56 +40,9 @@ pub fn derive_lattice_packed_witness_layout( )); } - let trace = PackingFactDomain::TraceRows { log_t }; - let ra_alphabet = one_hot_alphabet(log_k_chunk)?; - let mut specs = Vec::new(); - specs.extend((0..ra_layout.instruction()).map(|index| { - PackingFamilySpec::direct( - PackingFamilyId::InstructionRa { index }, - trace, - 1, - ra_alphabet, - ) - })); - specs.extend((0..ra_layout.bytecode()).map(|index| { - PackingFamilySpec::direct(PackingFamilyId::BytecodeRa { index }, trace, 1, ra_alphabet) - })); - specs.extend((0..ra_layout.ram()).map(|index| { - PackingFamilySpec::direct(PackingFamilyId::RamRa { index }, trace, 1, ra_alphabet) - })); - - let unsigned_inc_requirements = - unsigned_inc_validity_requirements(log_k_chunk).ok_or_else(|| { - invalid_lattice_config(format!( - "unsigned increment chunk reconstruction requires log_k_chunk to divide 64, got {log_k_chunk}", - )) - })?; - extend_validity_requirement_families(&mut specs, &unsigned_inc_requirements, trace)?; - - if config.lattice.field_inline.enabled { - extend_field_rd_inc_families(&mut specs, trace)?; - } - - if config.lattice.advice.untrusted { - specs.push(advice_family( - JoltAdviceKind::Untrusted, - require_advice_layout(precommitted, JoltAdviceKind::Untrusted)?, - )?); - } - - let _ = precommitted.bytecode.as_ref().ok_or_else(|| { - invalid_precommitted_schedule( - "lattice committed-program mode requires a bytecode claim-reduction layout", - ) - })?; - - let _ = precommitted.program_image.as_ref().ok_or_else(|| { - invalid_precommitted_schedule( - "lattice committed-program mode requires a program-image claim-reduction layout", - ) - })?; - - PackingWitnessLayout::new(specs).map_err(|error| invalid_lattice_config(error.to_string())) + let inputs = lattice_packing_inputs(config, log_t, log_k_chunk, ra_layout, precommitted)?; + require_committed_program_schedule(precommitted)?; + derive_jolt_lattice_packed_witness_layout(inputs).map_err(map_lattice_layout_error) } pub fn derive_lattice_packed_validity_requirements( @@ -102,38 +56,21 @@ pub fn derive_lattice_packed_validity_requirements( )); } - let mut requirements = unsigned_inc_validity_requirements(log_k_chunk).ok_or_else(|| { - invalid_lattice_config(format!( - "unsigned increment chunk reconstruction requires log_k_chunk to divide 64, got {log_k_chunk}", - )) - })?; - if config.lattice.field_inline.enabled { - requirements.extend(field_rd_inc_validity_requirements()); - } - if config.lattice.advice.untrusted { - let _ = require_advice_layout(precommitted, JoltAdviceKind::Untrusted)?; - requirements.push(advice_bytes_validity_requirement(JoltAdviceKind::Untrusted)); - } - - let _ = precommitted.bytecode.as_ref().ok_or_else(|| { - invalid_precommitted_schedule( - "lattice committed-program mode requires a bytecode claim-reduction layout", - ) - })?; - - let _ = precommitted.program_image.as_ref().ok_or_else(|| { - invalid_precommitted_schedule( - "lattice committed-program mode requires a program-image claim-reduction layout", - ) - })?; - - Ok(requirements) + let inputs = lattice_validity_inputs(config, log_k_chunk, precommitted)?; + require_committed_program_schedule(precommitted)?; + derive_jolt_lattice_packed_validity_requirements(inputs).map_err(map_lattice_layout_error) } pub fn lattice_protocol_config_for_packed_witness_layout( layout: &PackingWitnessLayout, -) -> JoltProtocolConfig { - let validity_requirements = lattice_validity_requirements_for_packed_witness_layout(layout); +) -> Result { + let field_rd_inc_byte_width = field_rd_inc_byte_width(layout)?; + let mut validity_requirements = + lattice_validity_requirements_for_packed_witness_layout(layout)?; + extend_layout_derived_field_rd_inc_validity( + field_rd_inc_byte_width, + &mut validity_requirements, + )?; let mut config = JoltProtocolConfig::for_zk(false).with_pcs_family(PcsFamily::Lattice); config.lattice = LatticeConfig { program_mode: ProgramMode::Committed, @@ -144,140 +81,21 @@ pub fn lattice_protocol_config_for_packed_witness_layout( validity_digest: Some(packing_validity_digest(&validity_requirements)), }, field_inline: FieldInlineLatticeConfig { - enabled: layout_has_field_rd_inc(layout), + enabled: field_rd_inc_byte_width.is_some(), }, advice: AdviceLatticeConfig { trusted: false, untrusted: layout_has_advice(layout, PackingAdviceKind::Untrusted), }, }; - config + Ok(config) } pub fn lattice_validity_requirements_for_packed_witness_layout( layout: &PackingWitnessLayout, -) -> Vec { - let mut requirements = layout - .families - .iter() - .filter_map(|family| { - let limbs = family.limbs; - let alphabet_size = family.alphabet.size(); - match family.id { - PackingFamilyId::UnsignedIncChunk { index } => { - Some(PackingValidityRequirement::exact_one_hot( - PackingFamilyId::UnsignedIncChunk { index }, - limbs, - alphabet_size, - )) - } - PackingFamilyId::UnsignedIncMsb => { - Some(PackingValidityRequirement::boolean_indicator( - PackingFamilyId::UnsignedIncMsb, - limbs, - alphabet_size, - 1, - )) - } - PackingFamilyId::FieldRdIncByte { index } => { - Some(PackingValidityRequirement::exact_one_hot( - PackingFamilyId::FieldRdIncByte { index }, - limbs, - alphabet_size, - )) - } - PackingFamilyId::AdviceBytes { kind, index } => { - Some(PackingValidityRequirement::exact_one_hot( - PackingFamilyId::AdviceBytes { kind, index }, - limbs, - alphabet_size, - )) - } - PackingFamilyId::BytecodeRegisterSelector { chunk, selector } => { - Some(PackingValidityRequirement::optional_one_hot( - PackingFamilyId::BytecodeRegisterSelector { chunk, selector }, - limbs, - alphabet_size, - )) - } - PackingFamilyId::BytecodeCircuitFlag { chunk, flag } => { - Some(PackingValidityRequirement::boolean_indicator( - PackingFamilyId::BytecodeCircuitFlag { chunk, flag }, - limbs, - alphabet_size, - 1, - )) - } - PackingFamilyId::BytecodeInstructionFlag { chunk, flag } => { - Some(PackingValidityRequirement::boolean_indicator( - PackingFamilyId::BytecodeInstructionFlag { chunk, flag }, - limbs, - alphabet_size, - 1, - )) - } - PackingFamilyId::BytecodeLookupSelector { chunk } => { - Some(PackingValidityRequirement::optional_one_hot( - PackingFamilyId::BytecodeLookupSelector { chunk }, - limbs, - alphabet_size, - )) - } - PackingFamilyId::BytecodeRafFlag { chunk } => { - Some(PackingValidityRequirement::boolean_indicator( - PackingFamilyId::BytecodeRafFlag { chunk }, - limbs, - alphabet_size, - 1, - )) - } - PackingFamilyId::BytecodeUnexpandedPcBytes { chunk } => { - Some(PackingValidityRequirement::exact_one_hot( - PackingFamilyId::BytecodeUnexpandedPcBytes { chunk }, - limbs, - alphabet_size, - )) - } - PackingFamilyId::BytecodeImmBytes { chunk } => { - Some(PackingValidityRequirement::exact_one_hot( - PackingFamilyId::BytecodeImmBytes { chunk }, - limbs, - alphabet_size, - )) - } - PackingFamilyId::ProgramImageInit => { - Some(PackingValidityRequirement::exact_one_hot( - PackingFamilyId::ProgramImageInit, - limbs, - alphabet_size, - )) - } - PackingFamilyId::InstructionRa { .. } - | PackingFamilyId::BytecodeRa { .. } - | PackingFamilyId::RamRa { .. } - | PackingFamilyId::FieldRdIncSign - | PackingFamilyId::BytecodeChunk { .. } - | PackingFamilyId::Custom { .. } => None, - } - }) - .collect::>(); - for family in &layout.families { - let PackingFamilyId::BytecodeCircuitFlag { chunk, flag } = &family.id else { - continue; - }; - let chunk = *chunk; - if *flag == CircuitFlags::Store as usize - && layout - .family(&PackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }) - .is_some() - { - requirements.push(PackingValidityRequirement::bytecode_store_rd_disjoint( - chunk, - CircuitFlags::Store as usize, - )); - } - } - requirements +) -> Result, VerifierError> { + claims_lattice_validity_requirements_for_packed_witness_layout(layout) + .map_err(|error| invalid_lattice_config(error.to_string())) } pub fn validate_lattice_packed_witness_validity_config( @@ -326,101 +144,147 @@ pub fn validate_lattice_packed_witness_layout_config( Ok(()) } -fn packed_family_is_precommitted(family: &PackingFamilyId) -> bool { - matches!( - family, - PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Trusted, - .. - } | PackingFamilyId::BytecodeChunk { .. } - | PackingFamilyId::BytecodeRegisterSelector { .. } - | PackingFamilyId::BytecodeCircuitFlag { .. } - | PackingFamilyId::BytecodeInstructionFlag { .. } - | PackingFamilyId::BytecodeLookupSelector { .. } - | PackingFamilyId::BytecodeRafFlag { .. } - | PackingFamilyId::BytecodeUnexpandedPcBytes { .. } - | PackingFamilyId::BytecodeImmBytes { .. } - | PackingFamilyId::ProgramImageInit - ) +fn lattice_packing_inputs<'a>( + config: &JoltProtocolConfig, + log_t: usize, + log_k_chunk: usize, + ra_layout: JoltRaPolynomialLayout, + precommitted: &'a PrecommittedSchedule, +) -> Result, VerifierError> { + let untrusted_advice = if config.lattice.advice.untrusted { + Some(require_advice_layout( + precommitted, + JoltAdviceKind::Untrusted, + )?) + } else { + None + }; + Ok(JoltLatticePackingInputs { + log_t, + log_k_chunk, + ra_layout, + field_rd_inc: field_rd_inc_packing(config), + untrusted_advice, + }) } -fn advice_family( - kind: JoltAdviceKind, - layout: &AdviceClaimReductionLayout, -) -> Result { - let packed_kind = packing_advice_kind(kind); - let requirement = advice_bytes_validity_requirement(kind); - Ok(PackingFamilySpec::direct( - requirement.family, - PackingFactDomain::AdviceBytes { - kind: packed_kind, - log_bytes: layout.advice_shape().total_vars() + 3, - }, - requirement.limbs, - packed_alphabet_with_size(requirement.alphabet_size)?, - )) +fn field_rd_inc_packing(config: &JoltProtocolConfig) -> Option { + config + .lattice + .field_inline + .enabled + .then(field_rd_inc_packing_config) } -fn extend_validity_requirement_families( - specs: &mut Vec, - requirements: &[PackingValidityRequirement], - domain: PackingFactDomain, +fn field_rd_inc_byte_width(layout: &PackingWitnessLayout) -> Result, VerifierError> { + let mut indices = layout + .families + .iter() + .filter_map( + |family| match JoltPackingFamilyId::from_physical_id(&family.id) { + Some(JoltPackingFamilyId::FieldRdIncByte { index }) => Some(index), + _ => None, + }, + ) + .collect::>(); + if indices.is_empty() { + return Ok(None); + } + indices.sort_unstable(); + for (expected, index) in indices.iter().copied().enumerate() { + if index != expected { + return Err(invalid_lattice_config( + "lattice field-inline byte families must be contiguous from zero", + )); + } + } + Ok(Some(indices.len())) +} + +#[cfg(feature = "field-inline")] +fn extend_layout_derived_field_rd_inc_validity( + byte_width: Option, + requirements: &mut Vec, ) -> Result<(), VerifierError> { - for requirement in requirements { - if matches!( - requirement.kind, - PackingValidityKind::BytecodeStoreRdDisjoint - | PackingValidityKind::FieldElementCanonicalBytes { .. } - ) { - continue; + if let Some(byte_width) = byte_width { + if byte_width != AkitaField::NUM_BYTES { + return Err(invalid_lattice_config( + "lattice field-inline byte family count must match the Akita field byte width", + )); } - specs.push(PackingFamilySpec::direct( - requirement.family.clone(), - domain, - requirement.limbs, - packed_alphabet_with_size(requirement.alphabet_size)?, + requirements.push(field_lattice::field_rd_inc_canonical_bytes_requirement( + byte_width, + AKITA_FIELD_MODULUS, )); } Ok(()) } -#[cfg(feature = "field-inline")] -fn field_rd_inc_validity_requirements() -> Vec { - let mut requirements = field_lattice::field_rd_inc_validity_requirements(AkitaField::NUM_BYTES); - requirements.push(field_lattice::field_rd_inc_canonical_bytes_requirement( - AkitaField::NUM_BYTES, - AKITA_FIELD_MODULUS, - )); - requirements +#[cfg(not(feature = "field-inline"))] +fn extend_layout_derived_field_rd_inc_validity( + byte_width: Option, + _requirements: &mut Vec, +) -> Result<(), VerifierError> { + if byte_width.is_some() { + return Err(invalid_lattice_config( + "lattice field-inline families require the field-inline feature", + )); + } + Ok(()) } -#[cfg(not(feature = "field-inline"))] -fn field_rd_inc_validity_requirements() -> Vec { - (0..AkitaField::NUM_BYTES) - .map(|index| { - PackingValidityRequirement::exact_one_hot( - PackingFamilyId::FieldRdIncByte { index }, - 1, - 256, - ) - }) - .collect() +fn lattice_validity_inputs<'a>( + config: &JoltProtocolConfig, + log_k_chunk: usize, + precommitted: &'a PrecommittedSchedule, +) -> Result, VerifierError> { + let untrusted_advice = if config.lattice.advice.untrusted { + Some(require_advice_layout( + precommitted, + JoltAdviceKind::Untrusted, + )?) + } else { + None + }; + Ok(JoltLatticeValidityInputs { + log_k_chunk, + field_rd_inc: field_rd_inc_packing(config), + untrusted_advice, + }) } #[cfg(feature = "field-inline")] -fn extend_field_rd_inc_families( - specs: &mut Vec, - domain: PackingFactDomain, -) -> Result<(), VerifierError> { - extend_validity_requirement_families(specs, &field_rd_inc_validity_requirements(), domain) +fn field_rd_inc_packing_config() -> FieldRdIncPacking { + FieldRdIncPacking { + byte_width: AkitaField::NUM_BYTES, + canonical_modulus: Some(AKITA_FIELD_MODULUS), + } } #[cfg(not(feature = "field-inline"))] -fn extend_field_rd_inc_families( - specs: &mut Vec, - domain: PackingFactDomain, +fn field_rd_inc_packing_config() -> FieldRdIncPacking { + FieldRdIncPacking { + byte_width: AkitaField::NUM_BYTES, + canonical_modulus: None, + } +} + +fn require_committed_program_schedule( + precommitted: &PrecommittedSchedule, ) -> Result<(), VerifierError> { - extend_validity_requirement_families(specs, &field_rd_inc_validity_requirements(), domain) + let _ = precommitted.bytecode.as_ref().ok_or_else(|| { + invalid_precommitted_schedule( + "lattice committed-program mode requires a bytecode claim-reduction layout", + ) + })?; + + let _ = precommitted.program_image.as_ref().ok_or_else(|| { + invalid_precommitted_schedule( + "lattice committed-program mode requires a program-image claim-reduction layout", + ) + })?; + + Ok(()) } fn require_advice_layout( @@ -434,55 +298,6 @@ fn require_advice_layout( }) } -fn packing_advice_kind(kind: JoltAdviceKind) -> PackingAdviceKind { - match kind { - JoltAdviceKind::Trusted => PackingAdviceKind::Trusted, - JoltAdviceKind::Untrusted => PackingAdviceKind::Untrusted, - } -} - -fn layout_has_field_rd_inc(layout: &PackingWitnessLayout) -> bool { - layout - .families - .iter() - .any(|family| matches!(family.id, PackingFamilyId::FieldRdIncByte { .. })) -} - -fn layout_has_advice(layout: &PackingWitnessLayout, kind: PackingAdviceKind) -> bool { - layout.families.iter().any(|family| { - matches!( - family.id, - PackingFamilyId::AdviceBytes { - kind: family_kind, - .. - } if family_kind == kind - ) - }) -} - -fn one_hot_alphabet(log_k_chunk: usize) -> Result { - match log_k_chunk { - 0 => Err(invalid_lattice_config( - "lattice one-hot chunk size must be nonzero", - )), - 1 => Ok(PackingAlphabet::Bit), - 8 => Ok(PackingAlphabet::Byte), - bits if bits < usize::BITS as usize => Ok(PackingAlphabet::Fixed { - size: 1usize << bits, - }), - _ => Err(invalid_lattice_config( - "lattice one-hot chunk size is too large", - )), - } -} - -pub(super) fn packed_alphabet_with_size(size: usize) -> Result { - match size { - 0 => Err(invalid_lattice_config( - "packed validity requirement alphabet size must be nonzero", - )), - 2 => Ok(PackingAlphabet::Bit), - 256 => Ok(PackingAlphabet::Byte), - size => Ok(PackingAlphabet::Fixed { size }), - } +fn map_lattice_layout_error(error: JoltLatticeLayoutError) -> VerifierError { + invalid_lattice_config(error.to_string()) } diff --git a/crates/jolt-verifier/src/stages/stage8/lattice_validity.rs b/crates/jolt-verifier/src/stages/stage8/lattice_validity.rs index 8fb1347280..4a6a7b66a5 100644 --- a/crates/jolt-verifier/src/stages/stage8/lattice_validity.rs +++ b/crates/jolt-verifier/src/stages/stage8/lattice_validity.rs @@ -1,5 +1,7 @@ use crate::{config::JoltProtocolConfig, stages::PrecommittedSchedule, VerifierError}; -use jolt_claims::protocols::jolt::formulas::dimensions::REGISTER_ADDRESS_BITS; +use jolt_claims::protocols::jolt::{ + formulas::dimensions::REGISTER_ADDRESS_BITS, JoltPackingFamilyId, +}; use jolt_field::Field; use jolt_openings::{ BatchOpeningClaim, BatchOpeningScheme, BatchOpeningStatement, CommitmentScheme, @@ -174,10 +176,11 @@ fn validate_bytecode_store_rd_disjoint_layout( requirement: &PackingValidityRequirement, ) -> Result { let chunk = bytecode_store_rd_disjoint_chunk(requirement)?; - let store_id = PackingFamilyId::BytecodeCircuitFlag { + let store_id: PackingFamilyId = JoltPackingFamilyId::BytecodeCircuitFlag { chunk, flag: CircuitFlags::Store as usize, - }; + } + .into(); let store = layout.family(&store_id).ok_or_else(|| { invalid_lattice_config(format!( "bytecode Store/Rd disjointness requires {store_id:?}" @@ -189,7 +192,8 @@ fn validate_bytecode_store_rd_disjoint_layout( )); } - let rd_id = PackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }; + let rd_id: PackingFamilyId = + JoltPackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }.into(); let rd = layout.family(&rd_id).ok_or_else(|| { invalid_lattice_config(format!("bytecode Store/Rd disjointness requires {rd_id:?}")) })?; @@ -213,12 +217,14 @@ fn validate_bytecode_store_rd_disjoint_layout( fn bytecode_store_rd_disjoint_chunk( requirement: &PackingValidityRequirement, ) -> Result { - let PackingFamilyId::BytecodeCircuitFlag { chunk, flag } = &requirement.family else { + let Some(JoltPackingFamilyId::BytecodeCircuitFlag { chunk, flag }) = + JoltPackingFamilyId::from_physical_id(&requirement.family) + else { return Err(invalid_lattice_config( "bytecode Store/Rd disjointness must be anchored on the Store circuit flag", )); }; - if *flag != CircuitFlags::Store as usize + if flag != CircuitFlags::Store as usize || requirement.limbs != 1 || requirement.alphabet_size != 2 { @@ -226,7 +232,7 @@ fn bytecode_store_rd_disjoint_chunk( "bytecode Store/Rd disjointness must be anchored on a boolean Store circuit flag", )); } - Ok(*chunk) + Ok(chunk) } fn validate_field_element_canonical_bytes_layout( @@ -240,9 +246,9 @@ fn validate_field_element_canonical_bytes_layout( )); } - match &requirement.family { - PackingFamilyId::FieldRdIncByte { index: 0 } => { - let first_id = PackingFamilyId::FieldRdIncByte { index: 0 }; + match JoltPackingFamilyId::from_physical_id(&requirement.family) { + Some(JoltPackingFamilyId::FieldRdIncByte { index: 0 }) => { + let first_id: PackingFamilyId = JoltPackingFamilyId::FieldRdIncByte { index: 0 }.into(); let first = layout.family(&first_id).ok_or_else(|| { invalid_lattice_config( "field-element canonical-byte validity requires FieldRdIncByte[0]", @@ -260,7 +266,8 @@ fn validate_field_element_canonical_bytes_layout( })?; let row_vars = power_of_two_log(rows, "field-element canonical-byte row count")?; for index in 1..byte_width { - let family_id = PackingFamilyId::FieldRdIncByte { index }; + let family_id: PackingFamilyId = + JoltPackingFamilyId::FieldRdIncByte { index }.into(); let family = layout.family(&family_id).ok_or_else(|| { invalid_lattice_config(format!( "field-element canonical-byte validity requires {family_id:?}" @@ -277,8 +284,8 @@ fn validate_field_element_canonical_bytes_layout( } Ok(row_vars) } - PackingFamilyId::BytecodeImmBytes { chunk } => { - let family_id = PackingFamilyId::BytecodeImmBytes { chunk: *chunk }; + Some(JoltPackingFamilyId::BytecodeImmBytes { chunk }) => { + let family_id: PackingFamilyId = JoltPackingFamilyId::BytecodeImmBytes { chunk }.into(); let family = layout.family(&family_id).ok_or_else(|| { invalid_lattice_config(format!( "field-element canonical-byte validity requires {family_id:?}" @@ -338,12 +345,13 @@ fn canonical_field_byte_location( "field-element canonical-byte index {byte_index} is outside byte width {byte_width}", ))); } - match &requirement.family { - PackingFamilyId::FieldRdIncByte { index: 0 } => { - Ok((PackingFamilyId::FieldRdIncByte { index: byte_index }, 0)) - } - PackingFamilyId::BytecodeImmBytes { chunk } => Ok(( - PackingFamilyId::BytecodeImmBytes { chunk: *chunk }, + match JoltPackingFamilyId::from_physical_id(&requirement.family) { + Some(JoltPackingFamilyId::FieldRdIncByte { index: 0 }) => Ok(( + JoltPackingFamilyId::FieldRdIncByte { index: byte_index }.into(), + 0, + )), + Some(JoltPackingFamilyId::BytecodeImmBytes { chunk }) => Ok(( + JoltPackingFamilyId::BytecodeImmBytes { chunk }.into(), byte_index, )), _ => Err(invalid_lattice_config(format!( @@ -379,7 +387,7 @@ pub(crate) fn field_element_canonical_factors( if start_symbol < 256 { factors.push(FieldCanonicalFactor::Range { byte_index, - family: family.clone(), + family, limb, start_symbol, }); @@ -796,7 +804,7 @@ where statement.kind ))); } - let family_id = statement.requirement.family.clone(); + let family_id = statement.requirement.family; let shape = validity_statement_shape(layout, statement, &family_id)?; let point_parts = split_validity_point(statement.kind, point, shape)?; let limb_weights = EqPolynomial::::evals(point_parts.limb, None); @@ -875,7 +883,7 @@ where })?; let (family_id, limb) = match &factor { FieldCanonicalFactor::Eq { family, limb, .. } - | FieldCanonicalFactor::Range { family, limb, .. } => (family.clone(), *limb), + | FieldCanonicalFactor::Range { family, limb, .. } => (*family, *limb), }; let family = layout.family(&family_id).ok_or_else(|| { invalid_lattice_config(format!( @@ -933,10 +941,11 @@ where F: Field, { let chunk = bytecode_store_rd_disjoint_chunk(&statement.requirement)?; - let store_id = PackingFamilyId::BytecodeCircuitFlag { + let store_id: PackingFamilyId = JoltPackingFamilyId::BytecodeCircuitFlag { chunk, flag: CircuitFlags::Store as usize, - }; + } + .into(); let store = layout.family(&store_id).ok_or_else(|| { invalid_lattice_config(format!( "bytecode Store/Rd disjointness requires {store_id:?}" @@ -961,7 +970,8 @@ where 0 => vec![PackingTerm::new(F::one(), store_id.physical_ref(), 0, 1) .with_row_point(point.to_vec())], 1 => { - let rd_id = PackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }; + let rd_id: PackingFamilyId = + JoltPackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }.into(); let rd = layout.family(&rd_id).ok_or_else(|| { invalid_lattice_config(format!("bytecode Store/Rd disjointness requires {rd_id:?}")) })?; diff --git a/crates/jolt-verifier/src/stages/stage8/lattice_validity_coverage.rs b/crates/jolt-verifier/src/stages/stage8/lattice_validity_coverage.rs index 8bc5e79a7f..b2694617ef 100644 --- a/crates/jolt-verifier/src/stages/stage8/lattice_validity_coverage.rs +++ b/crates/jolt-verifier/src/stages/stage8/lattice_validity_coverage.rs @@ -1,3 +1,4 @@ +use jolt_claims::protocols::jolt::JoltPackingFamilyId; use jolt_openings::{ PackingFamilyId, PackingValidityKind, PackingValidityRequirement, PackingViewFormula, }; @@ -90,10 +91,12 @@ fn validate_lattice_term_validity_coverage( fn core_jolt_ra_family(family: &PackingFamilyId) -> bool { matches!( - family, - PackingFamilyId::InstructionRa { .. } - | PackingFamilyId::BytecodeRa { .. } - | PackingFamilyId::RamRa { .. } + JoltPackingFamilyId::from_physical_id(family), + Some( + JoltPackingFamilyId::InstructionRa { .. } + | JoltPackingFamilyId::BytecodeRa { .. } + | JoltPackingFamilyId::RamRa { .. } + ) ) } @@ -120,8 +123,11 @@ fn requirement_covers_term( fn term_requires_canonical_bytes(family: &PackingFamilyId) -> bool { matches!( - family, - PackingFamilyId::FieldRdIncByte { .. } | PackingFamilyId::BytecodeImmBytes { .. } + JoltPackingFamilyId::from_physical_id(family), + Some( + JoltPackingFamilyId::FieldRdIncByte { .. } + | JoltPackingFamilyId::BytecodeImmBytes { .. } + ) ) } @@ -133,23 +139,28 @@ fn canonical_requirement_covers_term( let Ok(byte_width) = canonical_field_byte_width(requirement) else { return false; }; - match (&requirement.family, family) { + match ( + JoltPackingFamilyId::from_physical_id(&requirement.family), + JoltPackingFamilyId::from_physical_id(family), + ) { ( - PackingFamilyId::FieldRdIncByte { index: 0 }, - PackingFamilyId::FieldRdIncByte { index }, - ) => *index < byte_width && limb == 0, + Some(JoltPackingFamilyId::FieldRdIncByte { index: 0 }), + Some(JoltPackingFamilyId::FieldRdIncByte { index }), + ) => index < byte_width && limb == 0, ( - PackingFamilyId::BytecodeImmBytes { chunk: expected }, - PackingFamilyId::BytecodeImmBytes { chunk }, + Some(JoltPackingFamilyId::BytecodeImmBytes { chunk: expected }), + Some(JoltPackingFamilyId::BytecodeImmBytes { chunk }), ) => expected == chunk && limb < byte_width, _ => false, } } fn term_requires_bytecode_store_rd_disjoint(family: &PackingFamilyId) -> bool { - match family { - PackingFamilyId::BytecodeCircuitFlag { flag, .. } => *flag == CircuitFlags::Store as usize, - PackingFamilyId::BytecodeRegisterSelector { selector, .. } => *selector == 2, + match JoltPackingFamilyId::from_physical_id(family) { + Some(JoltPackingFamilyId::BytecodeCircuitFlag { flag, .. }) => { + flag == CircuitFlags::Store as usize + } + Some(JoltPackingFamilyId::BytecodeRegisterSelector { selector, .. }) => selector == 2, _ => false, } } @@ -161,22 +172,22 @@ fn bytecode_store_rd_disjoint_requirement_covers_term( let PackingValidityKind::BytecodeStoreRdDisjoint = requirement.kind else { return false; }; - let PackingFamilyId::BytecodeCircuitFlag { + let Some(JoltPackingFamilyId::BytecodeCircuitFlag { chunk: requirement_chunk, flag, - } = &requirement.family + }) = JoltPackingFamilyId::from_physical_id(&requirement.family) else { return false; }; - if *flag != CircuitFlags::Store as usize { + if flag != CircuitFlags::Store as usize { return false; } - match family { - PackingFamilyId::BytecodeCircuitFlag { chunk, flag } => { - requirement_chunk == chunk && *flag == CircuitFlags::Store as usize + match JoltPackingFamilyId::from_physical_id(family) { + Some(JoltPackingFamilyId::BytecodeCircuitFlag { chunk, flag }) => { + requirement_chunk == chunk && flag == CircuitFlags::Store as usize } - PackingFamilyId::BytecodeRegisterSelector { chunk, selector } => { - requirement_chunk == chunk && *selector == 2 + Some(JoltPackingFamilyId::BytecodeRegisterSelector { chunk, selector }) => { + requirement_chunk == chunk && selector == 2 } _ => false, } diff --git a/crates/jolt-verifier/src/stages/stage8/outputs.rs b/crates/jolt-verifier/src/stages/stage8/outputs.rs index bdcb955f2f..df13040cff 100644 --- a/crates/jolt-verifier/src/stages/stage8/outputs.rs +++ b/crates/jolt-verifier/src/stages/stage8/outputs.rs @@ -213,8 +213,17 @@ mod tests { )] use super::*; + #[cfg(feature = "akita")] + use jolt_claims::protocols::jolt::JoltPackingFamilyId; use jolt_claims::protocols::jolt::{JoltCommittedPolynomial, JoltOpeningId, JoltRelationId}; use jolt_field::{Fr, FromPrimitiveInt}; + #[cfg(feature = "akita")] + use jolt_openings::PackingFamilyId; + + #[cfg(feature = "akita")] + fn physical(family: JoltPackingFamilyId) -> PackingFamilyId { + family.into() + } #[test] fn logical_manifest_reports_ids_and_claim_modes() { @@ -275,8 +284,8 @@ mod tests { #[test] fn physical_manifest_resolves_lattice_packing_views_from_catalog() { use jolt_openings::{ - PackingAlphabet, PackingFactDomain, PackingFamilyId, PackingFamilySpec, - PackingViewCatalog, PackingViewEntry, PackingViewFormula, PackingWitnessLayout, + PackingAlphabet, PackingFactDomain, PackingFamilySpec, PackingViewCatalog, + PackingViewEntry, PackingViewFormula, PackingWitnessLayout, }; let id = Stage8OpeningId::from(JoltOpeningId::committed( @@ -293,7 +302,7 @@ mod tests { pcs_opening_point: Point::high_to_low(vec![Fr::from_u64(4)]), }; let layout = PackingWitnessLayout::new([PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncMsb, + physical(JoltPackingFamilyId::UnsignedIncMsb), PackingFactDomain::TraceRows { log_t: 0 }, 1, PackingAlphabet::Bit, @@ -304,27 +313,27 @@ mod tests { [PackingViewEntry::new( id, id, - PackingViewFormula::direct(PackingFamilyId::UnsignedIncMsb, 0, 1), + PackingViewFormula::direct(physical(JoltPackingFamilyId::UnsignedIncMsb), 0, 1), )], ) .unwrap_or_else(|error| panic!("test catalog should be valid: {error}")); - let physical = + let physical_manifest = Stage8PhysicalManifest::from_packed_view_catalog(&logical, &layout, &catalog) .unwrap_or_else(|error| panic!("packed view resolution should succeed: {error}")); - assert_eq!(physical.layout_digest, layout.digest); - assert_eq!(physical.openings[0].id, id); - assert_eq!(physical.openings[0].relation, id); + assert_eq!(physical_manifest.layout_digest, layout.digest); + assert_eq!(physical_manifest.openings[0].id, id); + assert_eq!(physical_manifest.openings[0].relation, id); assert!(matches!( - &physical.openings[0].view, + &physical_manifest.openings[0].view, PhysicalView::Packing { layout_digest, terms } if *layout_digest == layout.digest && terms.len() == 1 && terms[0].coefficient == Fr::from_u64(1) - && terms[0].family == PackingFamilyId::UnsignedIncMsb.physical_ref() + && terms[0].family == physical(JoltPackingFamilyId::UnsignedIncMsb).physical_ref() && terms[0].symbol == 1 )); } @@ -334,8 +343,8 @@ mod tests { fn physical_manifest_resolves_jolt_lattice_view_formulas() { use jolt_claims::protocols::jolt::byte_decode_terms; use jolt_openings::{ - PackingAlphabet, PackingFactDomain, PackingFamilyId, PackingFamilySpec, - PackingViewFormula, PackingWitnessLayout, + PackingAlphabet, PackingFactDomain, PackingFamilySpec, PackingViewFormula, + PackingWitnessLayout, }; let jolt_id = JoltOpeningId::committed( @@ -353,20 +362,20 @@ mod tests { pcs_opening_point: Point::high_to_low(vec![Fr::from_u64(4)]), }; let layout = PackingWitnessLayout::new([PackingFamilySpec::direct( - PackingFamilyId::BytecodeChunk { index: 0 }, + physical(JoltPackingFamilyId::BytecodeChunk { index: 0 }), PackingFactDomain::BytecodeRows { log_bytecode: 1 }, 8, PackingAlphabet::Byte, )]) .unwrap_or_else(|error| panic!("test layout should be valid: {error}")); - let physical = Stage8PhysicalManifest::from_jolt_lattice_view_formulas( + let physical_manifest = Stage8PhysicalManifest::from_jolt_lattice_view_formulas( &logical, &layout, [( Stage8OpeningId::from(jolt_id), PackingViewFormula::linear_decoded(byte_decode_terms::( - PackingFamilyId::BytecodeChunk { index: 0 }, + physical(JoltPackingFamilyId::BytecodeChunk { index: 0 }), 3, )), vec![Fr::from_u64(1)], @@ -374,18 +383,18 @@ mod tests { ) .unwrap_or_else(|error| panic!("lattice formula should resolve: {error}")); - assert_eq!(physical.layout_digest, layout.digest); - assert_eq!(physical.openings[0].id, id); - assert_eq!(physical.openings[0].relation, id); + assert_eq!(physical_manifest.layout_digest, layout.digest); + assert_eq!(physical_manifest.openings[0].id, id); + assert_eq!(physical_manifest.openings[0].relation, id); assert!(matches!( - &physical.openings[0].view, + &physical_manifest.openings[0].view, PhysicalView::Packing { layout_digest, terms } if *layout_digest == layout.digest && terms.len() == 256 && terms[7].coefficient == Fr::from_u64(7) - && terms[7].family == (PackingFamilyId::BytecodeChunk { index: 0 }).physical_ref() + && terms[7].family == physical(JoltPackingFamilyId::BytecodeChunk { index: 0 }).physical_ref() && terms[7].limb == 3 && terms[7].symbol == 7 && terms[7].row_point == vec![Fr::from_u64(1)] @@ -396,8 +405,8 @@ mod tests { #[test] fn physical_manifest_rejects_missing_jolt_lattice_formula() { use jolt_openings::{ - PackingAlphabet, PackingFactDomain, PackingFamilyId, PackingFamilySpec, - PackingViewError, PackingViewFormula, PackingWitnessLayout, + PackingAlphabet, PackingFactDomain, PackingFamilySpec, PackingViewError, + PackingViewFormula, PackingWitnessLayout, }; let expected_id = JoltOpeningId::committed( @@ -418,7 +427,7 @@ mod tests { pcs_opening_point: Point::high_to_low(vec![Fr::from_u64(4)]), }; let layout = PackingWitnessLayout::new([PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncMsb, + physical(JoltPackingFamilyId::UnsignedIncMsb), PackingFactDomain::TraceRows { log_t: 0 }, 1, PackingAlphabet::Bit, @@ -431,7 +440,11 @@ mod tests { &layout, [( Stage8OpeningId::from(supplied_id), - PackingViewFormula::::direct(PackingFamilyId::UnsignedIncMsb, 0, 1,), + PackingViewFormula::::direct( + physical(JoltPackingFamilyId::UnsignedIncMsb), + 0, + 1, + ), vec![Fr::from_u64(1)], )], ), @@ -443,8 +456,8 @@ mod tests { #[test] fn physical_manifest_rejects_masked_jolt_lattice_formula() { use jolt_openings::{ - PackingAlphabet, PackingFactDomain, PackingFamilyId, PackingFamilySpec, - PackingViewError, PackingViewFormula, PackingWitnessLayout, + PackingAlphabet, PackingFactDomain, PackingFamilySpec, PackingViewError, + PackingViewFormula, PackingWitnessLayout, }; let jolt_id = JoltOpeningId::committed( @@ -461,7 +474,7 @@ mod tests { pcs_opening_point: Point::high_to_low(vec![Fr::from_u64(4)]), }; let layout = PackingWitnessLayout::new([PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncMsb, + physical(JoltPackingFamilyId::UnsignedIncMsb), PackingFactDomain::TraceRows { log_t: 0 }, 1, PackingAlphabet::Bit, diff --git a/crates/jolt-verifier/src/stages/stage8/verify.rs b/crates/jolt-verifier/src/stages/stage8/verify.rs index d68fea8341..fffbbae39e 100644 --- a/crates/jolt-verifier/src/stages/stage8/verify.rs +++ b/crates/jolt-verifier/src/stages/stage8/verify.rs @@ -125,31 +125,14 @@ where if checked.zk { return Err(VerifierError::ExpectedCommittedProof { field: "stage8" }); } - let batch_result = PCS::verify_batch( + let output = verify_clear_batch_statement::( &preprocessing.pcs_setup, transcript, - &batch.statement, + batch, &proof.joint_opening_proof, - ) - .map_err(|error| VerifierError::FinalOpeningVerificationFailed { - reason: error.to_string(), - })?; - let mut coefficients = batch_result.coefficients; - coefficients.extend(verify_precommitted_opening_batches::( - &preprocessing.pcs_setup, - transcript, - &batch.precommitted_statements, &proof.lattice_precommitted_opening_proofs, - )?); - - Ok(Stage8Output::Clear(Stage8ClearOutput { - opening_claims: batch.opening_claims, - opening_ids: batch.opening_ids, - constraint_coefficients: coefficients, - pcs_opening_point: batch.pcs_opening_point, - joint_claim: batch_result.reduced_opening, - joint_commitment: batch_result.joint_commitment, - })) + )?; + Ok(Stage8Output::Clear(output)) } } } @@ -184,21 +167,48 @@ where return Err(VerifierError::ExpectedClearProof { field: "stage8" }); }; - let batch_result = PCS::verify_batch( + verify_clear_batch_statement::( &preprocessing.pcs_setup, transcript, - &batch.statement, + batch, &proof.joint_opening_proof, + &proof.lattice_precommitted_opening_proofs, ) - .map_err(|error| VerifierError::FinalOpeningVerificationFailed { - reason: error.to_string(), - })?; +} + +fn verify_clear_batch_statement( + setup: &PCS::VerifierSetup, + transcript: &mut T, + batch: Stage8ClearBatchStatement, + proof: &PCS::Proof, + precommitted_proofs: &[PCS::Proof], +) -> Result, VerifierError> +where + PCS: BatchOpeningScheme, + T: Transcript, +{ + if batch.precommitted_statements.len() != precommitted_proofs.len() { + return Err(VerifierError::FinalOpeningVerificationFailed { + reason: format!( + "expected {} precommitted opening proofs, got {}", + batch.precommitted_statements.len(), + precommitted_proofs.len() + ), + }); + } + + let batch_result = + PCS::verify_batch(setup, transcript, &batch.statement, proof).map_err(|error| { + VerifierError::FinalOpeningVerificationFailed { + reason: error.to_string(), + } + })?; let mut coefficients = batch_result.coefficients; coefficients.extend(verify_precommitted_opening_batches::( - &preprocessing.pcs_setup, + setup, transcript, &batch.precommitted_statements, - &proof.lattice_precommitted_opening_proofs, + precommitted_proofs, )?); Ok(Stage8ClearOutput { @@ -1032,6 +1042,173 @@ mod tests { use super::*; use jolt_field::{Fr, FromPrimitiveInt}; + use jolt_openings::{BatchOpeningResult, OpeningsError}; + use jolt_poly::{MultilinearPoly, Polynomial}; + + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + struct MainProofFailingPcs; + + impl jolt_crypto::Commitment for MainProofFailingPcs { + type Output = u64; + } + + impl CommitmentScheme for MainProofFailingPcs { + type Field = Fr; + type Proof = Fr; + type ProverSetup = (); + type VerifierSetup = (); + type Polynomial = Polynomial; + type OpeningHint = (); + type SetupParams = (); + + fn setup(_params: Self::SetupParams) -> (Self::ProverSetup, Self::VerifierSetup) { + ((), ()) + } + + fn verifier_setup(_prover_setup: &Self::ProverSetup) -> Self::VerifierSetup {} + + fn commit + ?Sized>( + _poly: &P, + _setup: &Self::ProverSetup, + ) -> (Self::Output, Self::OpeningHint) { + (0, ()) + } + + fn open( + _poly: &Self::Polynomial, + _point: &[Self::Field], + eval: Self::Field, + _setup: &Self::ProverSetup, + _hint: Option, + _transcript: &mut impl Transcript, + ) -> Self::Proof { + eval + } + + fn verify( + _commitment: &Self::Output, + _point: &[Self::Field], + _eval: Self::Field, + _proof: &Self::Proof, + _setup: &Self::VerifierSetup, + _transcript: &mut impl Transcript, + ) -> Result<(), OpeningsError> { + Ok(()) + } + + fn bind_opening_inputs( + _transcript: &mut impl Transcript, + _point: &[Self::Field], + _eval: &Self::Field, + ) { + } + } + + impl BatchOpeningScheme for MainProofFailingPcs { + fn prove_batch( + _setup: &Self::ProverSetup, + _transcript: &mut T, + statement: &BatchOpeningStatement, + _polynomials: &[Self::Polynomial], + _hints: Vec, + ) -> Result + where + T: Transcript, + { + statement + .claims + .first() + .map(|claim| claim.claim) + .ok_or(OpeningsError::VerificationFailed) + } + + fn verify_batch( + _setup: &Self::VerifierSetup, + _transcript: &mut T, + _statement: &BatchOpeningStatement, + _proof: &Self::Proof, + ) -> Result, OpeningsError> + where + T: Transcript, + { + Err(OpeningsError::InvalidBatch( + "main packed proof was checked first".to_string(), + )) + } + } + + #[test] + fn clear_batch_rejects_precommitted_count_before_main_proof() { + let id = final_opening_id(JoltCommittedPolynomial::RdInc).into(); + let point = vec![Fr::from_u64(2)]; + let statement = BatchOpeningStatement { + logical_point: point.clone(), + pcs_point: point.clone(), + layout_digest: [0; 32], + claims: vec![BatchOpeningClaim { + id, + relation: id, + commitment: 7, + claim: Fr::from_u64(5), + view: PhysicalView::Direct, + scale: Fr::from_u64(1), + }], + }; + let batch = Stage8ClearBatchStatement { + logical_manifest: Stage8LogicalManifest { + openings: vec![Stage8LogicalOpening { + id, + point: point.clone(), + claim: Some(Fr::from_u64(5)), + scale: Fr::from_u64(1), + }], + pcs_opening_point: Point::high_to_low(point.clone()), + }, + physical_manifest: Stage8PhysicalManifest::direct( + &Stage8LogicalManifest { + openings: Vec::new(), + pcs_opening_point: Point::high_to_low(point.clone()), + }, + [0; 32], + ), + opening_ids: vec![id], + opening_claims: Vec::new(), + pcs_opening_point: Point::high_to_low(point.clone()), + statement: statement.clone(), + precommitted_statements: vec![statement], + }; + let mut transcript = jolt_transcript::Blake2bTranscript::new(b"stage8-count-precheck"); + + let error = verify_clear_batch_statement::( + &(), + &mut transcript, + batch.clone(), + &Fr::from_u64(99), + &[], + ) + .expect_err("missing precommitted proof should reject before main packed proof"); + + assert!(matches!( + error, + VerifierError::FinalOpeningVerificationFailed { reason } + if reason.contains("expected 1 precommitted opening proofs, got 0") + )); + + let error = verify_clear_batch_statement::( + &(), + &mut transcript, + batch, + &Fr::from_u64(99), + &[Fr::from_u64(1), Fr::from_u64(2)], + ) + .expect_err("extra precommitted proof should reject before main packed proof"); + + assert!(matches!( + error, + VerifierError::FinalOpeningVerificationFailed { reason } + if reason.contains("expected 1 precommitted opening proofs, got 2") + )); + } #[test] fn committed_program_batch_entries_require_final_openings() { diff --git a/crates/jolt-verifier/tests/akita_prover_support.rs b/crates/jolt-verifier/tests/akita_prover_support.rs index 57a43c3720..d0ba08cbd8 100644 --- a/crates/jolt-verifier/tests/akita_prover_support.rs +++ b/crates/jolt-verifier/tests/akita_prover_support.rs @@ -5,44 +5,76 @@ )] use common::jolt_device::JoltDevice; +#[cfg(feature = "field-inline")] +use jolt_akita::AKITA_FIELD_MODULUS; use jolt_akita::{AkitaField, AkitaScheme, AkitaSetupParams}; +#[cfg(feature = "field-inline")] +use jolt_claims::protocols::field_inline::formulas::lattice as field_lattice; use jolt_claims::protocols::jolt::formulas::dimensions::REGISTER_ADDRESS_BITS; +use jolt_claims::protocols::jolt::{JoltAdviceKind, JoltPackingFamilyId}; #[cfg(feature = "field-inline")] use jolt_field::FixedByteSize; use jolt_openings::{ packing_validity_digest, CommitmentScheme, PackingAdviceKind, PackingAlphabet, PackingCellAddress, PackingFactDomain, PackingFamilyId, PackingFamilySpec, PackingProverSetup, - PackingVerifierSetup, PackingWitnessLayout, PackingWitnessSource, SparsePackingWitness, + PackingValidityRequirement, PackingVerifierSetup, PackingWitnessLayout, PackingWitnessSource, + SparsePackingWitness, }; use jolt_riscv::{ CapturedState, JoltInstructionKind, JoltInstructionRow, JoltTraceRow, NonMemoryState, NormalizedOperands, StoreState, }; use jolt_verifier::{ - akita::{ + prover_support::{ build_akita_packing_jolt_witness, commit_akita_packing_jolt_witness, - commit_akita_packing_witness, commit_akita_packing_witness_with_config, verify_akita_clear, - AkitaJoltProof, AkitaPackingBatchProof, AkitaPackingJoltWitnessInput, - AkitaPackingProverSetup, AkitaPackingValidityProofArtifacts, AkitaPackingVerifierSetup, - AkitaPackingWitnessArtifacts, AkitaVerifierPreprocessing, + commit_akita_packing_witness, commit_akita_packing_witness_with_config, + AkitaPackingBatchProof, AkitaPackingJoltWitnessInput, AkitaPackingProverSetup, + AkitaPackingValidityProofArtifacts, AkitaPackingVerifierSetup, + AkitaPackingWitnessArtifacts, }, stages::stage8::{ lattice_protocol_config_for_packed_witness_layout, lattice_validity_requirements_for_packed_witness_layout, }, - IncrementCommitmentMode, JoltProtocolConfig, ProgramMode, VerifierError, + verify_akita_clear, AkitaJoltProof, AkitaVerifierPreprocessing, IncrementCommitmentMode, + JoltProtocolConfig, ProgramMode, VerifierError, }; +fn physical(family: JoltPackingFamilyId) -> PackingFamilyId { + family.into() +} + +#[cfg(feature = "field-inline")] +fn expected_layout_protocol_validity_requirements( + layout: &PackingWitnessLayout, +) -> Vec { + let mut requirements = lattice_validity_requirements_for_packed_witness_layout(layout) + .expect("layout validity requirements should derive"); + requirements.push(field_lattice::field_rd_inc_canonical_bytes_requirement( + AkitaField::NUM_BYTES, + AKITA_FIELD_MODULUS, + )); + requirements +} + +#[cfg(not(feature = "field-inline"))] +fn expected_layout_protocol_validity_requirements( + layout: &PackingWitnessLayout, +) -> Vec { + lattice_validity_requirements_for_packed_witness_layout(layout) + .expect("layout validity requirements should derive") +} + fn tiny_layout() -> PackingWitnessLayout { let specs = vec![ PackingFamilySpec::direct( - PackingFamilyId::InstructionRa { index: 0 }, + physical(JoltPackingFamilyId::InstructionRa { index: 0 }), PackingFactDomain::TraceRows { log_t: 0 }, 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncMsb, + physical(JoltPackingFamilyId::UnsignedIncMsb), PackingFactDomain::TraceRows { log_t: 0 }, 1, PackingAlphabet::Bit, @@ -53,7 +85,7 @@ fn tiny_layout() -> PackingWitnessLayout { let mut specs = specs; specs.extend((0..AkitaField::NUM_BYTES).map(|index| { PackingFamilySpec::direct( - PackingFamilyId::FieldRdIncByte { index }, + physical(JoltPackingFamilyId::FieldRdIncByte { index }), PackingFactDomain::TraceRows { log_t: 0 }, 1, PackingAlphabet::Byte, @@ -132,7 +164,9 @@ fn akita_packing_setup( fn protocol_config_binds_layout_digest_and_dimension() { let layout = tiny_layout(); - let config = lattice_protocol_config_for_packed_witness_layout(&layout); + let config = lattice_protocol_config_for_packed_witness_layout(&layout) + .expect("layout protocol config should derive"); + let expected_requirements = expected_layout_protocol_validity_requirements(&layout); assert_eq!( config.lattice.packed_witness.layout_digest, @@ -141,9 +175,7 @@ fn protocol_config_binds_layout_digest_and_dimension() { assert_eq!(config.lattice.packed_witness.d_pack, Some(layout.dimension)); assert_eq!( config.lattice.packed_witness.validity_digest, - Some(packing_validity_digest( - &lattice_validity_requirements_for_packed_witness_layout(&layout) - )) + Some(packing_validity_digest(&expected_requirements)) ); assert_eq!(config.lattice.program_mode, ProgramMode::Committed); assert_eq!( @@ -183,34 +215,34 @@ fn commits_packed_witness_and_returns_verifier_payload() { fn commits_jolt_packed_witness_inputs_with_padding() { let specs = vec![ PackingFamilySpec::direct( - PackingFamilyId::InstructionRa { index: 0 }, + physical(JoltPackingFamilyId::InstructionRa { index: 0 }), PackingFactDomain::TraceRows { log_t: 1 }, 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::BytecodeRa { index: 0 }, + physical(JoltPackingFamilyId::BytecodeRa { index: 0 }), PackingFactDomain::TraceRows { log_t: 1 }, 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::RamRa { index: 0 }, + physical(JoltPackingFamilyId::RamRa { index: 0 }), PackingFactDomain::TraceRows { log_t: 1 }, 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncChunk { index: 0 }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 0 }), PackingFactDomain::TraceRows { log_t: 1 }, 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Untrusted, + physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Untrusted, index: 0, - }, + }), PackingFactDomain::AdviceBytes { kind: PackingAdviceKind::Untrusted, log_bytes: 2, @@ -224,7 +256,7 @@ fn commits_jolt_packed_witness_inputs_with_padding() { let mut specs = specs; specs.extend((0..AkitaField::NUM_BYTES).map(|index| { PackingFamilySpec::direct( - PackingFamilyId::FieldRdIncByte { index }, + physical(JoltPackingFamilyId::FieldRdIncByte { index }), PackingFactDomain::TraceRows { log_t: 1 }, 1, PackingAlphabet::Byte, @@ -292,7 +324,7 @@ fn commits_jolt_packed_witness_inputs_with_padding() { assert_eq!( witness .eval_direct_fact(&packed_cell_at( - PackingFamilyId::InstructionRa { index: 0 }, + physical(JoltPackingFamilyId::InstructionRa { index: 0 }), 0, 0, 0xaa, @@ -303,7 +335,7 @@ fn commits_jolt_packed_witness_inputs_with_padding() { assert_eq!( witness .eval_direct_fact(&packed_cell_at( - PackingFamilyId::BytecodeRa { index: 0 }, + physical(JoltPackingFamilyId::BytecodeRa { index: 0 }), 1, 0, 1, @@ -314,7 +346,7 @@ fn commits_jolt_packed_witness_inputs_with_padding() { assert_eq!( witness .eval_direct_fact(&packed_cell_at( - PackingFamilyId::RamRa { index: 0 }, + physical(JoltPackingFamilyId::RamRa { index: 0 }), 1, 0, 0x34 @@ -325,7 +357,7 @@ fn commits_jolt_packed_witness_inputs_with_padding() { assert_eq!( witness .eval_direct_fact(&packed_cell_at( - PackingFamilyId::UnsignedIncChunk { index: 0 }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 0 }), 0, 0, 3, @@ -336,10 +368,10 @@ fn commits_jolt_packed_witness_inputs_with_padding() { assert_eq!( witness .eval_direct_fact(&packed_cell_at( - PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Untrusted, + physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Untrusted, index: 0, - }, + }), 2, 0, 0, @@ -353,10 +385,10 @@ fn commits_jolt_packed_witness_inputs_with_padding() { fn build_jolt_packed_witness_rejects_precommitted_layout_families() { let forbidden_specs = [ PackingFamilySpec::direct( - PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Trusted, + physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Trusted, index: 0, - }, + }), PackingFactDomain::AdviceBytes { kind: PackingAdviceKind::Trusted, log_bytes: 0, @@ -365,16 +397,16 @@ fn build_jolt_packed_witness_rejects_precommitted_layout_families() { PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::BytecodeChunk { index: 0 }, + physical(JoltPackingFamilyId::BytecodeChunk { index: 0 }), PackingFactDomain::BytecodeRows { log_bytecode: 0 }, 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::BytecodeRegisterSelector { + physical(JoltPackingFamilyId::BytecodeRegisterSelector { chunk: 0, selector: 2, - }, + }), PackingFactDomain::BytecodeRows { log_bytecode: 0 }, 1, PackingAlphabet::Fixed { @@ -382,7 +414,7 @@ fn build_jolt_packed_witness_rejects_precommitted_layout_families() { }, ), PackingFamilySpec::direct( - PackingFamilyId::ProgramImageInit, + physical(JoltPackingFamilyId::ProgramImageInit), PackingFactDomain::ProgramImageWords { log_words: 0 }, 8, PackingAlphabet::Byte, @@ -419,7 +451,8 @@ fn configured_layout_mismatch_rejects_before_commit() { let (prover_setup, _) = akita_packing_setup(&layout, 1); let source = SparsePackingWitness::try_new(layout.clone(), Vec::new()) .expect("empty source should build"); - let mut config = lattice_protocol_config_for_packed_witness_layout(&layout); + let mut config = lattice_protocol_config_for_packed_witness_layout(&layout) + .expect("layout protocol config should derive"); config.lattice.packed_witness.layout_digest = Some([9; 32]); let error = commit_akita_packing_witness_with_config(config, &prover_setup, &source) @@ -448,7 +481,7 @@ fn akita_clear_verifier_surface_is_nameable() { &AkitaPackingWitnessArtifacts, &SparsePackingWitness, ) -> Result; - let _prove: ProveFn = jolt_verifier::akita::prove_akita_jolt_final_openings::< + let _prove: ProveFn = jolt_verifier::prover_support::prove_akita_jolt_final_openings::< TestTranscript, SparsePackingWitness, >; @@ -461,8 +494,9 @@ fn akita_clear_verifier_surface_is_nameable() { &AkitaPackingWitnessArtifacts, &SparsePackingWitness, ) -> Result; - let _prove_validity: ProveValidityFn = jolt_verifier::akita::prove_akita_jolt_packed_validity::< - TestTranscript, - SparsePackingWitness, - >; + let _prove_validity: ProveValidityFn = + jolt_verifier::prover_support::prove_akita_jolt_packed_validity::< + TestTranscript, + SparsePackingWitness, + >; } diff --git a/crates/jolt-verifier/tests/stage8_lattice.rs b/crates/jolt-verifier/tests/stage8_lattice.rs index e4093527a9..8ba0d705ce 100644 --- a/crates/jolt-verifier/tests/stage8_lattice.rs +++ b/crates/jolt-verifier/tests/stage8_lattice.rs @@ -8,8 +8,8 @@ use jolt_claims::protocols::jolt::{ formulas::dimensions::REGISTER_ADDRESS_BITS, formulas::ra::JoltRaPolynomialLayout, program_image_validity_requirement, unsigned_inc_chunk_opening, unsigned_inc_lower_chunk_count, unsigned_inc_lower_value_lattice_view_formula, unsigned_inc_msb_opening, - unsigned_inc_validity_requirements, JoltCommittedPolynomial, JoltOpeningId, JoltRelationId, - TracePolynomialOrder, + unsigned_inc_validity_requirements, JoltAdviceKind, JoltCommittedPolynomial, JoltOpeningId, + JoltPackingFamilyId, JoltRelationId, TracePolynomialOrder, }; use jolt_field::{FixedByteSize, Fr, FromPrimitiveInt}; use jolt_openings::{ @@ -41,6 +41,10 @@ use jolt_verifier::{ #[cfg(feature = "field-inline")] use jolt_claims::protocols::field_inline::formulas::claim_reductions::increments as field_increments; +fn physical(family: JoltPackingFamilyId) -> PackingFamilyId { + family.into() +} + fn lattice_config() -> JoltProtocolConfig { let mut config = JoltProtocolConfig::for_zk(false).with_pcs_family(PcsFamily::Lattice); config.lattice.program_mode = ProgramMode::Committed; @@ -179,16 +183,16 @@ fn bytecode_source_validity_layout(chunk: usize, log_bytecode: usize) -> Packing let domain = PackingFactDomain::BytecodeRows { log_bytecode }; PackingWitnessLayout::new([ PackingFamilySpec::direct( - PackingFamilyId::BytecodeCircuitFlag { + physical(JoltPackingFamilyId::BytecodeCircuitFlag { chunk, flag: CircuitFlags::Store as usize, - }, + }), domain, 1, PackingAlphabet::Bit, ), PackingFamilySpec::direct( - PackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }, + physical(JoltPackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }), domain, 1, PackingAlphabet::Fixed { @@ -212,37 +216,52 @@ fn derive_layout_includes_base_lattice_families() { .unwrap_or_else(|error| panic!("layout derivation should succeed: {error}")); assert!(layout - .family(&PackingFamilyId::InstructionRa { index: 0 }) + .family(&physical(JoltPackingFamilyId::InstructionRa { index: 0 })) + .is_some()); + assert!(layout + .family(&physical(JoltPackingFamilyId::RamRa { index: 0 })) .is_some()); assert!(layout - .family(&PackingFamilyId::RamRa { index: 0 }) + .family(&physical(JoltPackingFamilyId::UnsignedIncChunk { + index: 7 + })) .is_some()); assert!(layout - .family(&PackingFamilyId::UnsignedIncChunk { index: 7 }) + .family(&physical(JoltPackingFamilyId::UnsignedIncMsb)) .is_some()); - assert!(layout.family(&PackingFamilyId::UnsignedIncMsb).is_some()); assert!(layout - .family(&PackingFamilyId::BytecodeRegisterSelector { + .family(&physical(JoltPackingFamilyId::BytecodeRegisterSelector { chunk: 0, selector: 0, - }) + })) .is_none()); assert!(layout - .family(&PackingFamilyId::BytecodeCircuitFlag { chunk: 0, flag: 0 }) + .family(&physical(JoltPackingFamilyId::BytecodeCircuitFlag { + chunk: 0, + flag: 0 + })) .is_none()); assert!(layout - .family(&PackingFamilyId::BytecodeLookupSelector { chunk: 0 }) + .family(&physical(JoltPackingFamilyId::BytecodeLookupSelector { + chunk: 0 + })) .is_none()); assert!(layout - .family(&PackingFamilyId::BytecodeUnexpandedPcBytes { chunk: 0 }) + .family(&physical(JoltPackingFamilyId::BytecodeUnexpandedPcBytes { + chunk: 0 + })) .is_none()); assert!(layout - .family(&PackingFamilyId::BytecodeImmBytes { chunk: 0 }) + .family(&physical(JoltPackingFamilyId::BytecodeImmBytes { + chunk: 0 + })) .is_none()); assert!(layout - .family(&PackingFamilyId::BytecodeChunk { index: 0 }) + .family(&physical(JoltPackingFamilyId::BytecodeChunk { index: 0 })) + .is_none()); + assert!(layout + .family(&physical(JoltPackingFamilyId::ProgramImageInit)) .is_none()); - assert!(layout.family(&PackingFamilyId::ProgramImageInit).is_none()); assert_eq!(layout.audit().d_pack, layout.dimension); let mut matching_config = lattice_config(); @@ -281,19 +300,19 @@ fn validate_validity_config_rejects_mismatched_digest() { fn derive_validity_statements_matches_requirement_semantics() { let layout = PackingWitnessLayout::new([ PackingFamilySpec::direct( - PackingFamilyId::ProgramImageInit, + physical(JoltPackingFamilyId::ProgramImageInit), PackingFactDomain::ProgramImageWords { log_words: 2 }, 8, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::BytecodeLookupSelector { chunk: 0 }, + physical(JoltPackingFamilyId::BytecodeLookupSelector { chunk: 0 }), PackingFactDomain::BytecodeRows { log_bytecode: 3 }, 1, PackingAlphabet::Fixed { size: 8 }, ), PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncMsb, + physical(JoltPackingFamilyId::UnsignedIncMsb), PackingFactDomain::TraceRows { log_t: 4 }, 1, PackingAlphabet::Bit, @@ -301,13 +320,22 @@ fn derive_validity_statements_matches_requirement_semantics() { ]) .unwrap_or_else(|error| panic!("layout should build: {error}")); let requirements = vec![ - PackingValidityRequirement::exact_one_hot(PackingFamilyId::ProgramImageInit, 8, 256), + PackingValidityRequirement::exact_one_hot( + physical(JoltPackingFamilyId::ProgramImageInit), + 8, + 256, + ), PackingValidityRequirement::optional_one_hot( - PackingFamilyId::BytecodeLookupSelector { chunk: 0 }, + physical(JoltPackingFamilyId::BytecodeLookupSelector { chunk: 0 }), 1, 8, ), - PackingValidityRequirement::boolean_indicator(PackingFamilyId::UnsignedIncMsb, 1, 2, 1), + PackingValidityRequirement::boolean_indicator( + physical(JoltPackingFamilyId::UnsignedIncMsb), + 1, + 2, + 1, + ), ]; let statements = derive_lattice_packed_validity_statements(&layout, &requirements) @@ -376,8 +404,12 @@ fn derive_validity_statements_adds_unsigned_increment_chunk_and_msb_checks() { fn derive_validity_statements_adds_bytecode_store_rd_disjointness() { let chunk = 2; let layout = bytecode_source_validity_layout(chunk, 5); - let requirement = - PackingValidityRequirement::bytecode_store_rd_disjoint(chunk, CircuitFlags::Store as usize); + let requirement = PackingValidityRequirement::bytecode_store_rd_disjoint(physical( + JoltPackingFamilyId::BytecodeCircuitFlag { + chunk, + flag: CircuitFlags::Store as usize, + }, + )); let statements = derive_lattice_packed_validity_statements(&layout, std::slice::from_ref(&requirement)) @@ -400,7 +432,7 @@ fn derive_validity_statements_adds_bytecode_store_rd_disjointness() { fn derive_validity_statements_adds_field_element_canonical_bytes() { let layout = PackingWitnessLayout::new((0..2).map(|index| { PackingFamilySpec::direct( - PackingFamilyId::FieldRdIncByte { index }, + physical(JoltPackingFamilyId::FieldRdIncByte { index }), PackingFactDomain::TraceRows { log_t: 3 }, 1, PackingAlphabet::Byte, @@ -408,7 +440,7 @@ fn derive_validity_statements_adds_field_element_canonical_bytes() { })) .unwrap_or_else(|error| panic!("layout should build: {error}")); let requirement = PackingValidityRequirement::field_element_canonical_bytes( - PackingFamilyId::FieldRdIncByte { index: 0 }, + physical(JoltPackingFamilyId::FieldRdIncByte { index: 0 }), 2, 257, ); @@ -434,7 +466,7 @@ fn derive_validity_statements_adds_field_element_canonical_bytes() { fn derive_validity_statements_adds_bytecode_imm_canonical_bytes() { let chunk = 2; let layout = PackingWitnessLayout::new([PackingFamilySpec::direct( - PackingFamilyId::BytecodeImmBytes { chunk }, + physical(JoltPackingFamilyId::BytecodeImmBytes { chunk }), PackingFactDomain::BytecodeRows { log_bytecode: 3 }, 2, PackingAlphabet::Byte, @@ -462,14 +494,17 @@ fn derive_validity_statements_adds_bytecode_imm_canonical_bytes() { #[test] fn validity_batch_builder_lowers_cell_booleanity_to_packed_terms() { let layout = PackingWitnessLayout::new([PackingFamilySpec::direct( - PackingFamilyId::ProgramImageInit, + physical(JoltPackingFamilyId::ProgramImageInit), PackingFactDomain::ProgramImageWords { log_words: 1 }, 2, PackingAlphabet::Fixed { size: 4 }, )]) .unwrap_or_else(|error| panic!("layout should build: {error}")); - let requirement = - PackingValidityRequirement::exact_one_hot(PackingFamilyId::ProgramImageInit, 2, 4); + let requirement = PackingValidityRequirement::exact_one_hot( + physical(JoltPackingFamilyId::ProgramImageInit), + 2, + 4, + ); let statement = LatticePackedValidityStatement { requirement, kind: LatticePackedValidityStatementKind::CellBooleanity, @@ -528,7 +563,7 @@ fn validity_batch_builder_lowers_cell_booleanity_to_packed_terms() { assert_eq!(terms.len(), 8); let limb_weights = EqPolynomial::::evals(&point[1..2], None); let symbol_weights = EqPolynomial::::evals(&point[2..], None); - let term = find_physical_term(terms, PackingFamilyId::ProgramImageInit, 1, 3); + let term = find_physical_term(terms, physical(JoltPackingFamilyId::ProgramImageInit), 1, 3); assert_eq!(term.row_point, vec![Fr::from_u64(2)]); assert_eq!(term.coefficient, limb_weights[1] * symbol_weights[3]); } @@ -538,10 +573,12 @@ fn validity_batch_builder_lowers_bytecode_store_rd_disjointness_factors() { let chunk = 2; let layout = bytecode_source_validity_layout(chunk, 3); let statement = LatticePackedValidityStatement { - requirement: PackingValidityRequirement::bytecode_store_rd_disjoint( - chunk, - CircuitFlags::Store as usize, - ), + requirement: PackingValidityRequirement::bytecode_store_rd_disjoint(physical( + JoltPackingFamilyId::BytecodeCircuitFlag { + chunk, + flag: CircuitFlags::Store as usize, + }, + )), kind: LatticePackedValidityStatementKind::BytecodeStoreRdDisjoint, num_vars: 3, degree: 3, @@ -581,10 +618,10 @@ fn validity_batch_builder_lowers_bytecode_store_rd_disjointness_factors() { assert_eq!(terms.len(), 1); assert_eq!( terms[0].family, - PackingFamilyId::BytecodeCircuitFlag { + physical(JoltPackingFamilyId::BytecodeCircuitFlag { chunk, flag: CircuitFlags::Store as usize - } + }) .physical_ref() ); assert_eq!(terms[0].symbol, 1); @@ -596,7 +633,7 @@ fn validity_batch_builder_lowers_bytecode_store_rd_disjointness_factors() { assert_eq!(terms.len(), 1 << REGISTER_ADDRESS_BITS); let term = find_physical_term( terms, - PackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }, + physical(JoltPackingFamilyId::BytecodeRegisterSelector { chunk, selector: 2 }), 0, (1 << REGISTER_ADDRESS_BITS) - 1, ); @@ -608,7 +645,7 @@ fn validity_batch_builder_lowers_bytecode_store_rd_disjointness_factors() { fn validity_batch_builder_lowers_field_element_canonical_byte_factors() { let layout = PackingWitnessLayout::new((0..2).map(|index| { PackingFamilySpec::direct( - PackingFamilyId::FieldRdIncByte { index }, + physical(JoltPackingFamilyId::FieldRdIncByte { index }), PackingFactDomain::TraceRows { log_t: 2 }, 1, PackingAlphabet::Byte, @@ -616,7 +653,7 @@ fn validity_batch_builder_lowers_field_element_canonical_byte_factors() { })) .unwrap_or_else(|error| panic!("layout should build: {error}")); let requirement = PackingValidityRequirement::field_element_canonical_bytes( - PackingFamilyId::FieldRdIncByte { index: 0 }, + physical(JoltPackingFamilyId::FieldRdIncByte { index: 0 }), 2, 257, ); @@ -662,7 +699,7 @@ fn validity_batch_builder_lowers_field_element_canonical_byte_factors() { assert_eq!(terms.len(), 254); assert_eq!( terms[0].family, - PackingFamilyId::FieldRdIncByte { index: 1 }.physical_ref() + physical(JoltPackingFamilyId::FieldRdIncByte { index: 1 }).physical_ref() ); assert_eq!(terms[0].symbol, 2); assert_eq!(terms[253].symbol, 255); @@ -673,7 +710,7 @@ fn validity_batch_builder_lowers_field_element_canonical_byte_factors() { assert_eq!(terms.len(), 1); assert_eq!( terms[0].family, - PackingFamilyId::FieldRdIncByte { index: 1 }.physical_ref() + physical(JoltPackingFamilyId::FieldRdIncByte { index: 1 }).physical_ref() ); assert_eq!(terms[0].symbol, 1); @@ -683,7 +720,7 @@ fn validity_batch_builder_lowers_field_element_canonical_byte_factors() { assert_eq!(terms.len(), 255); assert_eq!( terms[0].family, - PackingFamilyId::FieldRdIncByte { index: 0 }.physical_ref() + physical(JoltPackingFamilyId::FieldRdIncByte { index: 0 }).physical_ref() ); assert_eq!(terms[0].symbol, 1); assert_eq!(terms[254].symbol, 255); @@ -693,7 +730,7 @@ fn validity_batch_builder_lowers_field_element_canonical_byte_factors() { fn validity_batch_builder_lowers_bytecode_imm_canonical_byte_factors() { let chunk = 2; let layout = PackingWitnessLayout::new([PackingFamilySpec::direct( - PackingFamilyId::BytecodeImmBytes { chunk }, + physical(JoltPackingFamilyId::BytecodeImmBytes { chunk }), PackingFactDomain::BytecodeRows { log_bytecode: 2 }, 2, PackingAlphabet::Byte, @@ -742,7 +779,7 @@ fn validity_batch_builder_lowers_bytecode_imm_canonical_byte_factors() { assert_eq!(terms.len(), 254); assert_eq!( terms[0].family, - PackingFamilyId::BytecodeImmBytes { chunk }.physical_ref() + physical(JoltPackingFamilyId::BytecodeImmBytes { chunk }).physical_ref() ); assert_eq!(terms[0].limb, 1); assert_eq!(terms[0].symbol, 2); @@ -755,7 +792,7 @@ fn validity_batch_builder_lowers_bytecode_imm_canonical_byte_factors() { assert_eq!(terms.len(), 1); assert_eq!( terms[0].family, - PackingFamilyId::BytecodeImmBytes { chunk }.physical_ref() + physical(JoltPackingFamilyId::BytecodeImmBytes { chunk }).physical_ref() ); assert_eq!(terms[0].limb, 1); assert_eq!(terms[0].symbol, 1); @@ -766,7 +803,7 @@ fn validity_batch_builder_lowers_bytecode_imm_canonical_byte_factors() { assert_eq!(terms.len(), 255); assert_eq!( terms[0].family, - PackingFamilyId::BytecodeImmBytes { chunk }.physical_ref() + physical(JoltPackingFamilyId::BytecodeImmBytes { chunk }).physical_ref() ); assert_eq!(terms[0].limb, 0); assert_eq!(terms[0].symbol, 1); @@ -777,14 +814,14 @@ fn validity_batch_builder_lowers_bytecode_imm_canonical_byte_factors() { #[test] fn derive_validity_statements_rejects_layout_requirement_mismatch() { let layout = PackingWitnessLayout::new([PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncMsb, + physical(JoltPackingFamilyId::UnsignedIncMsb), PackingFactDomain::TraceRows { log_t: 4 }, 1, PackingAlphabet::Bit, )]) .unwrap_or_else(|error| panic!("layout should build: {error}")); let requirements = [PackingValidityRequirement::exact_one_hot( - PackingFamilyId::UnsignedIncMsb, + physical(JoltPackingFamilyId::UnsignedIncMsb), 8, 256, )]; @@ -803,7 +840,7 @@ fn validity_coverage_accepts_bound_decoded_families() { JoltRelationId::ProgramImageClaimReduction, )); let formula = PackingViewFormula::linear_decoded(byte_decode_terms::( - PackingFamilyId::ProgramImageInit, + physical(JoltPackingFamilyId::ProgramImageInit), 0, )); @@ -821,7 +858,7 @@ fn validity_coverage_rejects_unbound_decoded_families() { JoltRelationId::ProgramImageClaimReduction, )); let formula = PackingViewFormula::linear_decoded(byte_decode_terms::( - PackingFamilyId::ProgramImageInit, + physical(JoltPackingFamilyId::ProgramImageInit), 0, )); @@ -839,11 +876,11 @@ fn validity_coverage_requires_canonical_byte_requirements_for_field_bytes() { JoltRelationId::BytecodeClaimReduction, )); let formula = PackingViewFormula::linear_decoded(byte_decode_terms::( - PackingFamilyId::BytecodeImmBytes { chunk: 0 }, + physical(JoltPackingFamilyId::BytecodeImmBytes { chunk: 0 }), 0, )); let one_hot = PackingValidityRequirement::exact_one_hot( - PackingFamilyId::BytecodeImmBytes { chunk: 0 }, + physical(JoltPackingFamilyId::BytecodeImmBytes { chunk: 0 }), AkitaField::NUM_BYTES, 256, ); @@ -884,24 +921,28 @@ fn validity_coverage_requires_bytecode_store_rd_disjointness() { JoltRelationId::BytecodeClaimReduction, )); let formula = PackingViewFormula::::direct( - PackingFamilyId::BytecodeCircuitFlag { + physical(JoltPackingFamilyId::BytecodeCircuitFlag { chunk: 0, flag: CircuitFlags::Store as usize, - }, + }), 0, 1, ); let store_flag = PackingValidityRequirement::boolean_indicator( - PackingFamilyId::BytecodeCircuitFlag { + physical(JoltPackingFamilyId::BytecodeCircuitFlag { chunk: 0, flag: CircuitFlags::Store as usize, - }, + }), 1, 2, 1, ); - let disjoint = - PackingValidityRequirement::bytecode_store_rd_disjoint(0, CircuitFlags::Store as usize); + let disjoint = PackingValidityRequirement::bytecode_store_rd_disjoint(physical( + JoltPackingFamilyId::BytecodeCircuitFlag { + chunk: 0, + flag: CircuitFlags::Store as usize, + }, + )); assert!(matches!( validate_lattice_view_validity_coverage( @@ -926,7 +967,7 @@ fn validity_coverage_allows_core_ra_families() { )); let formula = PackingViewFormula::linear_decoded(vec![PackingViewTerm::new( Fr::from_u64(1), - PackingFamilyId::InstructionRa { index: 0 }, + physical(JoltPackingFamilyId::InstructionRa { index: 0 }), 0, 0, )]); @@ -938,13 +979,17 @@ fn validity_coverage_allows_core_ra_families() { #[test] fn validity_coverage_checks_boolean_indicator_symbol() { let id = Stage8OpeningId::from(unsigned_inc_msb_opening()); - let requirement = - PackingValidityRequirement::boolean_indicator(PackingFamilyId::UnsignedIncMsb, 1, 2, 1); + let requirement = PackingValidityRequirement::boolean_indicator( + physical(JoltPackingFamilyId::UnsignedIncMsb), + 1, + 2, + 1, + ); validate_lattice_view_validity_coverage( &[( id, - PackingViewFormula::::direct(PackingFamilyId::UnsignedIncMsb, 0, 1), + PackingViewFormula::::direct(physical(JoltPackingFamilyId::UnsignedIncMsb), 0, 1), Vec::new(), )], std::slice::from_ref(&requirement), @@ -956,7 +1001,7 @@ fn validity_coverage_checks_boolean_indicator_symbol() { &[( id, PackingViewFormula::::direct( - PackingFamilyId::UnsignedIncMsb, + physical(JoltPackingFamilyId::UnsignedIncMsb), 0, 0, ), @@ -985,7 +1030,7 @@ fn derive_layout_uses_unsigned_increment_validity_requirements() { for requirement in unsigned_inc_validity_requirements(8) .unwrap_or_else(|| panic!("unsigned increment requirements should derive")) { - let family_id = requirement.family.clone(); + let family_id = requirement.family; let family = layout .family(&family_id) .unwrap_or_else(|| panic!("validity family {family_id:?} should be present")); @@ -1007,7 +1052,7 @@ fn derive_layout_excludes_committed_bytecode_source_requirements() { .unwrap_or_else(|error| panic!("validity requirements should derive: {error}")); for requirement in bytecode_validity_requirements(0, AkitaField::NUM_BYTES) { - let family_id = requirement.family.clone(); + let family_id = requirement.family; assert!(layout.family(&family_id).is_none()); assert!(!validity_requirements.contains(&requirement)); } @@ -1041,7 +1086,7 @@ fn jolt_lattice_resolver_weights_ra_symbols_by_address_point() { PackingViewFormula::LinearDecoded { terms, .. } if terms.len() == 4 && terms[2].coefficient == expected_weights[2] - && terms[2].family == PackingFamilyId::InstructionRa { index: 1 } + && terms[2].family == physical(JoltPackingFamilyId::InstructionRa { index: 1 }) && terms[2].limb == 0 && terms[2].symbol == 2 )); @@ -1064,8 +1109,12 @@ fn field_inline_rd_inc_resolves_to_packed_byte_families() { let terms = linear_decoded_terms(&formulas[0].1); assert_eq!(terms.len(), AkitaField::NUM_BYTES * 256); - let byte_1_symbol_3 = - find_lattice_term(terms, PackingFamilyId::FieldRdIncByte { index: 1 }, 0, 3); + let byte_1_symbol_3 = find_lattice_term( + terms, + physical(JoltPackingFamilyId::FieldRdIncByte { index: 1 }), + 0, + 3, + ); assert_eq!(byte_1_symbol_3.coefficient, Fr::from_u64(3 * 256)); } @@ -1097,8 +1146,8 @@ fn jolt_lattice_resolver_keeps_precommitted_finals_out_of_w_pack() { PackingViewFormula::LinearDecoded { terms, .. } if terms.len() == 256 && terms[7].coefficient == Fr::from_u64(7) - && terms[7].family == (PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Untrusted, + && terms[7].family == physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Untrusted, index: 0, }) && terms[7].limb == 0 @@ -1134,7 +1183,7 @@ fn jolt_lattice_resolver_lowers_unsigned_increment_chunk_and_msb_outputs() { assert_eq!( find_lattice_term( chunk_terms, - PackingFamilyId::UnsignedIncChunk { index: 7 }, + physical(JoltPackingFamilyId::UnsignedIncChunk { index: 7 }), 0, 3, ) @@ -1151,10 +1200,10 @@ fn jolt_lattice_resolver_lowers_unsigned_increment_chunk_and_msb_outputs() { ) .unwrap_or_else(|error| panic!("sign view should resolve: {error}")), PackingViewFormula::Direct { - family: PackingFamilyId::UnsignedIncMsb, + family, limb: 0, symbol: 1 - } + } if family == physical(JoltPackingFamilyId::UnsignedIncMsb) )); } @@ -1202,28 +1251,28 @@ fn jolt_lattice_physical_manifest_lowers_supported_ra_view() { let expected_weights = EqPolynomial::::evals(&point[..2], None); let logical = logical_manifest(id, point); let layout = PackingWitnessLayout::new([PackingFamilySpec::direct( - PackingFamilyId::InstructionRa { index: 1 }, + physical(JoltPackingFamilyId::InstructionRa { index: 1 }), PackingFactDomain::TraceRows { log_t: 1 }, 1, PackingAlphabet::Fixed { size: 4 }, )]) .unwrap_or_else(|error| panic!("test layout should be valid: {error}")); - let physical = + let physical_manifest = jolt_lattice_physical_manifest(&logical, &layout, 2, &precommitted_schedule(None)) .unwrap_or_else(|error| panic!("physical manifest should resolve: {error}")); - assert_eq!(physical.layout_digest, layout.digest); - assert_eq!(physical.openings[0].id, Stage8OpeningId::from(id)); + assert_eq!(physical_manifest.layout_digest, layout.digest); + assert_eq!(physical_manifest.openings[0].id, Stage8OpeningId::from(id)); assert!(matches!( - &physical.openings[0].view, + &physical_manifest.openings[0].view, PhysicalView::Packing { layout_digest, terms } if *layout_digest == layout.digest && terms.len() == 4 && terms[2].coefficient == expected_weights[2] - && terms[2].family == (PackingFamilyId::InstructionRa { index: 1 }).physical_ref() + && terms[2].family == physical(JoltPackingFamilyId::InstructionRa { index: 1 }).physical_ref() && terms[2].limb == 0 && terms[2].symbol == 2 )); @@ -1256,7 +1305,7 @@ fn jolt_lattice_physical_manifest_rejects_dense_increment_view() { JoltRelationId::IncClaimReduction, ); let layout = PackingWitnessLayout::new([PackingFamilySpec::direct( - PackingFamilyId::UnsignedIncMsb, + physical(JoltPackingFamilyId::UnsignedIncMsb), PackingFactDomain::TraceRows { log_t: 0 }, 1, PackingAlphabet::Bit, @@ -1302,7 +1351,12 @@ fn jolt_lattice_physical_manifest_resolves_field_inline_rd_inc() { let PhysicalView::Packing { terms, .. } = &manifest.openings[0].view else { panic!("field rd inc should lower to a packing view"); }; - let term = find_physical_term(terms, PackingFamilyId::FieldRdIncByte { index: 1 }, 0, 3); + let term = find_physical_term( + terms, + physical(JoltPackingFamilyId::FieldRdIncByte { index: 1 }), + 0, + 3, + ); assert_eq!(term.coefficient, Fr::from_u64(3 * 256)); assert_eq!(term.row_point, row_point); } @@ -1328,10 +1382,10 @@ fn layout_config_mismatch_rejects() { fn layout_config_rejects_precommitted_packed_families() { let precommitted_specs = [ PackingFamilySpec::direct( - PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Trusted, + physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Trusted, index: 0, - }, + }), PackingFactDomain::AdviceBytes { kind: PackingAdviceKind::Trusted, log_bytes: 1, @@ -1340,25 +1394,25 @@ fn layout_config_rejects_precommitted_packed_families() { PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::BytecodeChunk { index: 0 }, + physical(JoltPackingFamilyId::BytecodeChunk { index: 0 }), PackingFactDomain::BytecodeRows { log_bytecode: 1 }, 1, PackingAlphabet::Byte, ), PackingFamilySpec::direct( - PackingFamilyId::BytecodeCircuitFlag { + physical(JoltPackingFamilyId::BytecodeCircuitFlag { chunk: 0, flag: CircuitFlags::Store as usize, - }, + }), PackingFactDomain::BytecodeRows { log_bytecode: 1 }, 1, PackingAlphabet::Bit, ), PackingFamilySpec::direct( - PackingFamilyId::BytecodeRegisterSelector { + physical(JoltPackingFamilyId::BytecodeRegisterSelector { chunk: 0, selector: 2, - }, + }), PackingFactDomain::BytecodeRows { log_bytecode: 1 }, 1, PackingAlphabet::Fixed { @@ -1366,7 +1420,7 @@ fn layout_config_rejects_precommitted_packed_families() { }, ), PackingFamilySpec::direct( - PackingFamilyId::ProgramImageInit, + physical(JoltPackingFamilyId::ProgramImageInit), PackingFactDomain::ProgramImageWords { log_words: 1 }, 8, PackingAlphabet::Byte, @@ -1413,10 +1467,10 @@ fn untrusted_advice_layout_requires_precommitted_schedule() { ) .unwrap_or_else(|error| panic!("layout derivation should succeed: {error}")); assert!(layout - .family(&PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Untrusted, + .family(&physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Untrusted, index: 0, - }) + })) .is_some()); } @@ -1435,15 +1489,19 @@ fn field_inline_layout_uses_separate_rd_inc_families() { ) .unwrap_or_else(|error| panic!("layout derivation should succeed: {error}")); - assert!(layout.family(&PackingFamilyId::UnsignedIncMsb).is_some()); - assert!(layout.family(&PackingFamilyId::FieldRdIncSign).is_none()); assert!(layout - .family(&PackingFamilyId::FieldRdIncByte { index: 7 }) + .family(&physical(JoltPackingFamilyId::UnsignedIncMsb)) .is_some()); assert!(layout - .family(&PackingFamilyId::FieldRdIncByte { + .family(&physical(JoltPackingFamilyId::FieldRdIncSign)) + .is_none()); + assert!(layout + .family(&physical(JoltPackingFamilyId::FieldRdIncByte { index: 7 })) + .is_some()); + assert!(layout + .family(&physical(JoltPackingFamilyId::FieldRdIncByte { index: AkitaField::NUM_BYTES - 1 - }) + })) .is_some()); } @@ -1462,17 +1520,17 @@ fn untrusted_advice_uses_non_trace_domain() { .unwrap_or_else(|error| panic!("layout derivation should succeed: {error}")); assert!(layout - .family(&PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Trusted, + .family(&physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Trusted, index: 0, - }) + })) .is_none()); let untrusted = layout - .family(&PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Untrusted, + .family(&physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Untrusted, index: 0, - }) + })) .unwrap_or_else(|| panic!("untrusted advice family should be present")); assert!(matches!( untrusted.domain, @@ -1483,18 +1541,24 @@ fn untrusted_advice_uses_non_trace_domain() { )); assert!(layout - .family(&PackingFamilyId::BytecodeRegisterSelector { + .family(&physical(JoltPackingFamilyId::BytecodeRegisterSelector { chunk: 0, selector: 0, - }) + })) + .is_none()); + assert!(layout + .family(&physical(JoltPackingFamilyId::BytecodeLookupSelector { + chunk: 0 + })) .is_none()); assert!(layout - .family(&PackingFamilyId::BytecodeLookupSelector { chunk: 0 }) + .family(&physical(JoltPackingFamilyId::BytecodeImmBytes { + chunk: 0 + })) .is_none()); assert!(layout - .family(&PackingFamilyId::BytecodeImmBytes { chunk: 0 }) + .family(&physical(JoltPackingFamilyId::ProgramImageInit)) .is_none()); - assert!(layout.family(&PackingFamilyId::ProgramImageInit).is_none()); } #[cfg(feature = "field-inline")] @@ -1520,44 +1584,57 @@ fn single_packed_witness_layout_includes_all_supported_lattice_families() { assert_eq!(audit.cells_by_domain.bytecode_rows, 0); assert_eq!(audit.cells_by_domain.program_image_words, 0); assert!(audit.cells_by_domain.advice_bytes > 0); - assert!(layout.family(&PackingFamilyId::UnsignedIncMsb).is_some()); assert!(layout - .family(&PackingFamilyId::UnsignedIncChunk { index: 7 }) + .family(&physical(JoltPackingFamilyId::UnsignedIncMsb)) + .is_some()); + assert!(layout + .family(&physical(JoltPackingFamilyId::UnsignedIncChunk { + index: 7 + })) .is_some()); assert!(layout - .family(&PackingFamilyId::FieldRdIncByte { index: 0 }) + .family(&physical(JoltPackingFamilyId::FieldRdIncByte { index: 0 })) .is_some()); assert!(layout - .family(&PackingFamilyId::FieldRdIncByte { + .family(&physical(JoltPackingFamilyId::FieldRdIncByte { index: AkitaField::NUM_BYTES - 1, - }) + })) .is_some()); assert!(layout - .family(&PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Trusted, + .family(&physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Trusted, index: 0, - }) + })) .is_none()); assert!(layout - .family(&PackingFamilyId::AdviceBytes { - kind: PackingAdviceKind::Untrusted, + .family(&physical(JoltPackingFamilyId::AdviceBytes { + kind: JoltAdviceKind::Untrusted, index: 0, - }) + })) .is_some()); assert!(layout - .family(&PackingFamilyId::BytecodeRegisterSelector { + .family(&physical(JoltPackingFamilyId::BytecodeRegisterSelector { chunk: 0, selector: 2, - }) + })) + .is_none()); + assert!(layout + .family(&physical(JoltPackingFamilyId::BytecodeCircuitFlag { + chunk: 0, + flag: 0 + })) .is_none()); assert!(layout - .family(&PackingFamilyId::BytecodeCircuitFlag { chunk: 0, flag: 0 }) + .family(&physical(JoltPackingFamilyId::BytecodeLookupSelector { + chunk: 0 + })) .is_none()); assert!(layout - .family(&PackingFamilyId::BytecodeLookupSelector { chunk: 0 }) + .family(&physical(JoltPackingFamilyId::BytecodeImmBytes { + chunk: 0 + })) .is_none()); assert!(layout - .family(&PackingFamilyId::BytecodeImmBytes { chunk: 0 }) + .family(&physical(JoltPackingFamilyId::ProgramImageInit)) .is_none()); - assert!(layout.family(&PackingFamilyId::ProgramImageInit).is_none()); } diff --git a/crates/jolt-verifier/tests/statistical_independence/zk.rs b/crates/jolt-verifier/tests/statistical_independence/zk.rs index 5e5c978952..4c1191fe84 100644 --- a/crates/jolt-verifier/tests/statistical_independence/zk.rs +++ b/crates/jolt-verifier/tests/statistical_independence/zk.rs @@ -135,6 +135,11 @@ impl StableZkProofShape { let JoltProofClaims::Zk { blindfold_proof } = &case.proof.claims else { panic!("ZK statistical fixture must carry a BlindFold proof"); }; + let commitments = case + .proof + .commitments + .as_dory() + .expect("ZK statistical fixture must use Dory commitments"); Self { public_io: case.public_io.clone(), @@ -145,9 +150,9 @@ impl StableZkProofShape { one_hot_config: case.proof.one_hot_config, trace_polynomial_order: case.proof.trace_polynomial_order, commitment_shape: CommitmentShape { - instruction_ra: case.proof.commitments.ra.instruction.len(), - ram_ra: case.proof.commitments.ra.ram.len(), - bytecode_ra: case.proof.commitments.ra.bytecode.len(), + instruction_ra: commitments.ra.instruction.len(), + ram_ra: commitments.ra.ram.len(), + bytecode_ra: commitments.ra.bytecode.len(), has_untrusted_advice: case.proof.untrusted_advice_commitment.is_some(), }, stage_shapes: committed_stage_shapes(&case.proof.stages), @@ -279,15 +284,16 @@ fn collect_jolt_proof_statistics( let JoltProofClaims::Zk { blindfold_proof } = &proof.claims else { panic!("ZK statistical fixture must carry a BlindFold proof"); }; - - tracker.record_append("pcs.commitment.rd_inc", &proof.commitments.rd_inc); - tracker.record_append("pcs.commitment.ram_inc", &proof.commitments.ram_inc); - tracker.record_append_positions( - "pcs.commitment.instruction_ra", - &proof.commitments.ra.instruction, - ); - tracker.record_append_positions("pcs.commitment.ram_ra", &proof.commitments.ra.ram); - tracker.record_append_positions("pcs.commitment.bytecode_ra", &proof.commitments.ra.bytecode); + let commitments = proof + .commitments + .as_dory() + .expect("ZK statistical fixture must use Dory commitments"); + + tracker.record_append("pcs.commitment.rd_inc", &commitments.rd_inc); + tracker.record_append("pcs.commitment.ram_inc", &commitments.ram_inc); + tracker.record_append_positions("pcs.commitment.instruction_ra", &commitments.ra.instruction); + tracker.record_append_positions("pcs.commitment.ram_ra", &commitments.ra.ram); + tracker.record_append_positions("pcs.commitment.bytecode_ra", &commitments.ra.bytecode); if let Some(commitment) = &proof.untrusted_advice_commitment { tracker.record_append("pcs.commitment.untrusted_advice", commitment); }