From d7e46e7bf7ebc41c0ce9ce2f0bfed2984ad7936a Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Fri, 20 Mar 2026 16:26:08 +0100 Subject: [PATCH 01/68] bump to gr1cs interface --- Cargo.toml | 9 ++- src/crypto/blake3_crh/fields.rs | 35 +++++++++++ src/crypto/blake3_crh/mod.rs | 70 +++++++++++++++++++++ src/crypto/merkle/blake3.rs | 5 +- src/crypto/mod.rs | 1 + src/lib.rs | 1 - src/relations/description.rs | 36 ++++++----- src/relations/r1cs/hashchain/mod.rs | 2 +- src/relations/r1cs/hashchain/relation.rs | 18 ++++-- src/relations/r1cs/hashchain/synthesizer.rs | 2 +- src/relations/r1cs/mod.rs | 26 +++++--- src/serialize.rs | 11 ++-- 12 files changed, 171 insertions(+), 45 deletions(-) create mode 100644 src/crypto/blake3_crh/fields.rs create mode 100644 src/crypto/blake3_crh/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 7e3e30b..3a3440a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" ark-crypto-primitives = { version = "0.5.0", features = [ "merkle_tree", "crh", - "r1cs", + "constraints", "sponge", ] } ark-ff = "0.5.0" @@ -29,10 +29,13 @@ ark-codes = { git = "https://github.com/dmpierre/ark-codes.git" } [patch.crates-io] -ark-crypto-primitives = { git = "https://github.com/benbencik/crypto-primitives.git", branch = "smallfp-absorb-trait" } +ark-crypto-primitives = { git = "https://github.com/benbencik/crypto-primitives.git", branch = "smallfp-absorb-clean" } ark-ff = { git = "https://github.com/arkworks-rs/algebra.git" } ark-poly = { git = "https://github.com/arkworks-rs/algebra.git" } ark-serialize = { git = "https://github.com/arkworks-rs/algebra.git" } +ark-r1cs-std = { git = "https://github.com/arkworks-rs/r1cs-std" } +ark-relations = { git = "https://github.com/arkworks-rs/snark.git" } +ark-snark = { git = "https://github.com/arkworks-rs/snark.git" } # resolve transitive pull of spongefish from efficient-sumcheck [patch."https://github.com/arkworks-rs/spongefish"] @@ -41,7 +44,7 @@ spongefish = { git = "https://github.com/z-tech/spongefish.git", branch = "small [dev-dependencies] ark-bls12-381 = "0.5.0" ark-bn254 = "0.5.0" -criterion = "0.7" +criterion = "0.8" [features] default = ["asm"] diff --git a/src/crypto/blake3_crh/fields.rs b/src/crypto/blake3_crh/fields.rs new file mode 100644 index 0000000..0cc9417 --- /dev/null +++ b/src/crypto/blake3_crh/fields.rs @@ -0,0 +1,35 @@ +use ark_ff::Field; +use ark_serialize::CanonicalSerialize; +use ark_std::rand::RngCore; +use core::borrow::Borrow; +use core::marker::PhantomData; + +use ark_crypto_primitives::{crh::CRHScheme, Error}; + +use super::GenericDigest; + +/// Blake3 leaf hash that takes field elements as input. +#[derive(Clone)] +pub struct Blake3F { + _f: PhantomData, +} + +impl CRHScheme for Blake3F { + type Input = [F]; + type Output = GenericDigest<32>; + type Parameters = (); + + fn setup(_: &mut R) -> Result { + Ok(()) + } + + fn evaluate>( + (): &Self::Parameters, + input: T, + ) -> Result { + let mut buf = Vec::new(); + input.borrow().serialize_compressed(&mut buf)?; + let output: [_; 32] = blake3::hash(&buf).into(); + Ok(output.into()) + } +} diff --git a/src/crypto/blake3_crh/mod.rs b/src/crypto/blake3_crh/mod.rs new file mode 100644 index 0000000..404033b --- /dev/null +++ b/src/crypto/blake3_crh/mod.rs @@ -0,0 +1,70 @@ +pub mod fields; + +use ark_crypto_primitives::{crh::TwoToOneCRHScheme, sponge::Absorb, Error}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::rand::RngCore; +use core::borrow::Borrow; + +/// A generic fixed-size digest (copied from whir). +#[derive(Clone, Debug, Eq, PartialEq, Hash, CanonicalSerialize, CanonicalDeserialize)] +pub struct GenericDigest(pub [u8; N]); + +impl Default for GenericDigest { + fn default() -> Self { + Self([0; N]) + } +} + +impl AsRef<[u8]> for GenericDigest { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl From<[u8; N]> for GenericDigest { + fn from(value: [u8; N]) -> Self { + Self(value) + } +} + +impl Absorb for GenericDigest { + fn to_sponge_bytes(&self, dest: &mut Vec) { + dest.extend_from_slice(&self.0); + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + dest.push(F::from_be_bytes_mod_order(&self.0)); + } +} + +/// Blake3 two-to-one hash for internal Merkle tree nodes. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Blake3; + +impl TwoToOneCRHScheme for Blake3 { + type Input = GenericDigest<32>; + type Output = GenericDigest<32>; + type Parameters = (); + + fn setup(_: &mut R) -> Result { + Ok(()) + } + + fn evaluate>( + (): &Self::Parameters, + left_input: T, + right_input: T, + ) -> Result { + let output: [_; 32] = + blake3::hash(&[left_input.borrow().0, right_input.borrow().0].concat()).into(); + Ok(output.into()) + } + + fn compress>( + parameters: &Self::Parameters, + left_input: T, + right_input: T, + ) -> Result { + Self::evaluate(parameters, left_input, right_input) + } +} diff --git a/src/crypto/merkle/blake3.rs b/src/crypto/merkle/blake3.rs index 963a7d4..e6ca88c 100644 --- a/src/crypto/merkle/blake3.rs +++ b/src/crypto/merkle/blake3.rs @@ -1,7 +1,6 @@ use super::parameters::MerkleTreeParams; -use ark_crypto_primitives::crh::blake3::fields::Blake3F; -use ark_crypto_primitives::crh::blake3::Blake3; -use ark_crypto_primitives::crh::blake3::GenericDigest; +use crate::crypto::blake3_crh::fields::Blake3F; +use crate::crypto::blake3_crh::{Blake3, GenericDigest}; use ark_crypto_primitives::{ crh::{CRHScheme, TwoToOneCRHScheme}, merkle_tree::{Config as MerkleConfig, IdentityDigestConverter}, diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index bdf9eb8..6a3489a 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1 +1,2 @@ +pub mod blake3_crh; pub mod merkle; diff --git a/src/lib.rs b/src/lib.rs index 698dbdc..c05cc00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -733,7 +733,6 @@ impl< )?; (rt[0] == computed_mt.root()).ok_or_err(DeciderError::MerkleRoot)?; (mt[0].root() == computed_mt.root()).ok_or_err(DeciderError::MerkleTrapDoor)?; - (mt[0].leaf_nodes == computed_mt.leaf_nodes).ok_or_err(DeciderError::MerkleRoot)?; let f_hat = DenseMultilinearExtension::from_evaluations_slice( log2(self.code.code_len()) as usize, diff --git a/src/relations/description.rs b/src/relations/description.rs index 032dfeb..16a749d 100644 --- a/src/relations/description.rs +++ b/src/relations/description.rs @@ -1,5 +1,5 @@ use ark_ff::Field; -use ark_relations::r1cs::{ConstraintMatrices, ConstraintSynthesizer, ConstraintSystem}; +use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystem, R1CS_PREDICATE_LABEL}; use serde::Serialize; #[derive(Serialize)] @@ -37,22 +37,26 @@ impl SerializableConstraintMatrices { .generate_constraints(constraint_system.clone()) .unwrap(); constraint_system.finalize(); - let matrices: ConstraintMatrices = constraint_system.to_matrices().unwrap(); - let serializable = SerializableConstraintMatrices::from(matrices); + + let cs = constraint_system.into_inner().unwrap(); + let all_matrices = cs.to_matrices().unwrap(); + let r1cs_matrices = all_matrices + .get(R1CS_PREDICATE_LABEL) + .expect("R1CS predicate must exist"); + + let num_constraints = cs + .get_predicate_num_constraints(R1CS_PREDICATE_LABEL) + .unwrap_or(0); + + let serializable = SerializableConstraintMatrices { + num_instance_variables: cs.num_instance_variables(), + num_witness_variables: cs.num_witness_variables(), + num_constraints, + a: Self::serialize_nested_field(r1cs_matrices[0].clone()), + b: Self::serialize_nested_field(r1cs_matrices[1].clone()), + c: Self::serialize_nested_field(r1cs_matrices[2].clone()), + }; let serialized = serde_json::to_string(&serializable).unwrap(); serialized.into_bytes() } } - -impl From> for SerializableConstraintMatrices { - fn from(m: ConstraintMatrices) -> Self { - Self { - num_instance_variables: m.num_instance_variables, - num_witness_variables: m.num_witness_variables, - num_constraints: m.num_constraints, - a: SerializableConstraintMatrices::serialize_nested_field(m.a), - b: SerializableConstraintMatrices::serialize_nested_field(m.b), - c: SerializableConstraintMatrices::serialize_nested_field(m.c), - } - } -} diff --git a/src/relations/r1cs/hashchain/mod.rs b/src/relations/r1cs/hashchain/mod.rs index 45c664c..4f34fe3 100644 --- a/src/relations/r1cs/hashchain/mod.rs +++ b/src/relations/r1cs/hashchain/mod.rs @@ -12,7 +12,7 @@ use ark_crypto_primitives::{ }; use ark_ff::PrimeField; use ark_r1cs_std::fields::fp::FpVar; -use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; +use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystem}; pub use config::HashChainConfig; pub use instance::HashChainInstance; pub use relation::compute_hash_chain; diff --git a/src/relations/r1cs/hashchain/relation.rs b/src/relations/r1cs/hashchain/relation.rs index 6742e0a..e681158 100644 --- a/src/relations/r1cs/hashchain/relation.rs +++ b/src/relations/r1cs/hashchain/relation.rs @@ -4,7 +4,7 @@ use ark_crypto_primitives::{ }; use ark_ff::{Field, PrimeField}; use ark_r1cs_std::fields::fp::FpVar; -use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef}; +use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef}; use ark_serialize::CanonicalSerialize; use ark_std::marker::PhantomData; @@ -94,14 +94,22 @@ where .unwrap(); constraint_system.finalize(); - let cs = constraint_system.into_inner().unwrap(); + // Extract assignments via the ref (borrow the inner CS) + let x = constraint_system + .borrow() + .map(|cs| cs.instance_assignment().unwrap().to_vec()) + .unwrap(); + let w = constraint_system + .borrow() + .map(|cs| cs.witness_assignment().unwrap().to_vec()) + .unwrap(); Self { - constraint_system: ConstraintSystemRef::new(cs.clone()), + constraint_system, config: hash_config, instance, witness, - x: cs.instance_assignment, - w: cs.witness_assignment, + x, + w, _crhs_scheme: PhantomData, _crhs_scheme_gadget: PhantomData, } diff --git a/src/relations/r1cs/hashchain/synthesizer.rs b/src/relations/r1cs/hashchain/synthesizer.rs index beb4a90..511b1ff 100644 --- a/src/relations/r1cs/hashchain/synthesizer.rs +++ b/src/relations/r1cs/hashchain/synthesizer.rs @@ -1,7 +1,7 @@ use ark_crypto_primitives::crh::{CRHScheme, CRHSchemeGadget}; use ark_ff::{Field, PrimeField}; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, fields::fp::FpVar}; -use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; +use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; use ark_std::marker::PhantomData; use crate::relations::r1cs::hashchain::{HashChainInstance, HashChainWitness}; diff --git a/src/relations/r1cs/mod.rs b/src/relations/r1cs/mod.rs index 0232a7e..6377cfe 100644 --- a/src/relations/r1cs/mod.rs +++ b/src/relations/r1cs/mod.rs @@ -1,7 +1,7 @@ pub mod hashchain; use ark_ff::Field; -use ark_relations::r1cs::ConstraintSystemRef; +use ark_relations::gr1cs::ConstraintSystemRef; use efficient_sumcheck::{hypercube::Hypercube, order_strategy::AscendingOrder}; use crate::error::WARPError; @@ -27,21 +27,31 @@ impl TryFrom> for R1CS { type Error = WARPError; fn try_from(cs: ConstraintSystemRef) -> Result { - let matrices = cs.to_matrices().unwrap(); + use ark_relations::gr1cs::R1CS_PREDICATE_LABEL; + + let inner = cs.into_inner().unwrap(); + let all_matrices = inner.to_matrices().unwrap(); + let r1cs_matrices = all_matrices + .get(R1CS_PREDICATE_LABEL) + .expect("R1CS predicate must exist"); + + let num_constraints = inner + .get_predicate_num_constraints(R1CS_PREDICATE_LABEL) + .unwrap_or(0); // number of constraints should be to be power of 2 - let m = matrices.num_constraints.next_power_of_two(); - let n = matrices.num_instance_variables + matrices.num_witness_variables; - let k = matrices.num_witness_variables; + let m = num_constraints.next_power_of_two(); + let n = inner.num_instance_variables() + inner.num_witness_variables(); + let k = inner.num_witness_variables(); // both `unwrap()` calls below are safe since warp/lib.rs forbids compiling on platforms // with 16-bits pointers width let log_m = m.ilog2().try_into().unwrap(); let log_n = n.ilog2().try_into().unwrap(); - let mut a = matrices.a.into_iter(); - let mut b = matrices.b.into_iter(); - let mut c = matrices.c.into_iter(); + let mut a = r1cs_matrices[0].clone().into_iter(); + let mut b = r1cs_matrices[1].clone().into_iter(); + let mut c = r1cs_matrices[2].clone().into_iter(); let mut p = vec![]; for _ in 0..m { // when there are no constraints left, we store an empty one diff --git a/src/serialize.rs b/src/serialize.rs index e03ceff..5d5bb0d 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -29,9 +29,9 @@ pub struct AccWitnessSerializer< F: Field + PrimeField, MT: Config + From<[u8; 32]>>, > { - pub td: Vec, pub f: Vec, pub w: Vec, + _mt: std::marker::PhantomData, } impl + From<[u8; 32]>>> @@ -41,13 +41,10 @@ impl + Fr assert_eq!(acc_witness.0.len(), 1); assert_eq!(acc_witness.1.len(), 1); assert_eq!(acc_witness.2.len(), 1); - let f = acc_witness.1[0].clone(); - assert_eq!(f.len(), acc_witness.0[0].leaf_nodes.len()); - let w = acc_witness.2[0].clone(); Self { - td: acc_witness.0[0].clone().leaf_nodes, - f, - w, + f: acc_witness.1[0].clone(), + w: acc_witness.2[0].clone(), + _mt: std::marker::PhantomData, } } } From 6b753e5591f75d14b64934df265d2b0e2d5951de Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Wed, 25 Mar 2026 17:18:22 +0100 Subject: [PATCH 02/68] chkpt --- src/ior.rs | 61 ++++ src/lib.rs | 581 +++++++++++++++++++--------------- src/protocol/domainsep/mod.rs | 32 +- src/protocol/mod.rs | 1 + src/protocol/query.rs | 182 +++++++++++ src/serialize.rs | 79 ++--- src/traits.rs | 32 +- src/types.rs | 106 +++++++ src/utils/mod.rs | 24 -- 9 files changed, 733 insertions(+), 365 deletions(-) create mode 100644 src/ior.rs create mode 100644 src/protocol/query.rs create mode 100644 src/types.rs diff --git a/src/ior.rs b/src/ior.rs new file mode 100644 index 0000000..4654fe2 --- /dev/null +++ b/src/ior.rs @@ -0,0 +1,61 @@ +use ark_ff::Field; +use spongefish::{ProverState, VerifierState}; + +use crate::error::{DeciderError, ProverError, VerifierError}; + +/// An interactive oracle reduction transforms a claim on an input relation +/// into a claim on an (ideally simpler) output relation, +/// through prover/verifier interaction. +/// +/// This trait captures the general pattern described in the WARP paper: +/// InputRelation --IOR--> OutputRelation +/// +/// The prover produces a proof alongside the output claim. +/// The verifier, given the input instance and proof, derives the output instance. +pub trait InteractiveOracleReduction { + /// The relation being reduced FROM (input claim). + type InputInstance; + type InputWitness; + + /// The relation being reduced TO (output claim). + type OutputInstance; + type OutputWitness; + + /// Proof messages produced during this reduction step. + type Proof; + + /// Parameters needed for this reduction (e.g., index, code, Merkle params). + type Parameters; + + /// Prover side of the reduction: given an input claim (instance + witness), + /// interact with the transcript and produce an output claim + proof. + fn reduce_prove( + params: &Self::Parameters, + prover_state: &mut ProverState, + input_instance: &Self::InputInstance, + input_witness: &Self::InputWitness, + ) -> Result<(Self::OutputInstance, Self::OutputWitness, Self::Proof), ProverError>; + + /// Verifier side of the reduction: given an input instance + proof, + /// interact with the transcript and derive the output instance. + fn reduce_verify<'a>( + params: &Self::Parameters, + verifier_state: &mut VerifierState<'a>, + input_instance: &Self::InputInstance, + proof: &Self::Proof, + ) -> Result; +} + +/// A relation that can be checked directly without further reduction. +/// This is the "base case" — the decider checks the final accumulator. +pub trait Decidable { + type Instance; + type Witness; + type Parameters; + + fn decide( + params: &Self::Parameters, + instance: &Self::Instance, + witness: &Self::Witness, + ) -> Result<(), DeciderError>; +} diff --git a/src/lib.rs b/src/lib.rs index c05cc00..8797c2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #![allow(clippy::assign_op_pattern)] // generated by SmallFpConfig derive macro use crate::error::WARPError; use crate::traits::AccumulationScheme; +use crate::types::{AccumulatorInstance, AccumulatorWitness, PesatOutput, WARPParams, WARPProof}; use ark_codes::traits::LinearCode; use ark_crypto_primitives::{ crh::{CRHScheme, TwoToOneCRHScheme}, @@ -23,11 +24,10 @@ use efficient_sumcheck::{ order_strategy::AscendingOrder, }; use protocol::domainsep::parse_statement; +use protocol::query::QueryIndices; use relations::{r1cs::R1CSConstraints, BundledPESAT}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; -use utils::binary_field_elements_to_usize; -use utils::byte_to_binary_field_array; use utils::scale_and_sum; use utils::{ concat_slices, @@ -45,6 +45,7 @@ pub mod protocol; pub mod relations; pub mod serialize; pub mod traits; +pub mod types; pub mod utils; use ark_crypto_primitives::Error; @@ -139,12 +140,7 @@ impl BoolResult for bool { } pub struct WARP, C: LinearCode + Clone, MT: Config> { - _f: PhantomData, - config: WARPConfig, - code: C, - p: P, - mt_leaf_hash_params: ::Parameters, - mt_two_to_one_hash_params: ::Parameters, + pub params: WARPParams, } impl< @@ -162,12 +158,14 @@ impl< mt_two_to_one_hash_params: ::Parameters, ) -> WARP { Self { - _f: PhantomData, - config, - code, - p, - mt_leaf_hash_params, - mt_two_to_one_hash_params, + params: WARPParams { + _f: PhantomData, + config, + code, + p, + mt_leaf_hash_params, + mt_two_to_one_hash_params, + }, } } } @@ -177,106 +175,29 @@ impl< P: Clone + BundledPESAT, Config = (usize, usize, usize)>, // m, n, k C: LinearCode + Clone, MT: Config + From<[u8; 32]>>, - > AccumulationScheme for WARP + > WARP { - type Index = P; - type ProverKey = (P, usize, usize, usize); - type VerifierKey = (usize, usize, usize); - type Instances = Vec>; - type Witnesses = Vec>; - - type AccumulatorInstances = ( - Vec, - Vec>, - Vec, - (Vec>, Vec>), - Vec, - ); // (rt, \alpha, \mu, \beta (\tau, x), \eta) - - type AccumulatorWitnesses = (Vec>, Vec>, Vec>); // (td, f, w) - - // (rt_0, \mu_i, \nu_0, \nu_i, auth_0, auth_j, ((f_i(x_j)))) - type Proof = ( - MT::InnerDigest, - Vec, - F, - Vec, - Vec>, - Vec>>, - Vec>, - ); - - fn index( - prover_state: &mut ProverState, - index: Self::Index, - ) -> spongefish::VerificationResult<(Self::ProverKey, Self::VerifierKey)> { - let (m, n, k) = index.config(); - // initialize prover state for fs - // TODO for R1CS - prover_state.public_message(&index.description()); - prover_state.prover_message(&F::from(m as u32)); - prover_state.prover_message(&F::from(n as u32)); - prover_state.prover_message(&F::from(k as u32)); - Ok(((index.clone(), m, n, k), (m, n, k))) - } - - fn prove( + /// Phase 2: PESAT Reduction + /// + /// Encodes fresh witnesses into codewords, commits via Merkle tree, + /// absorbs the commitment and code evaluations, and derives τ challenges. + fn pesat_reduce( &self, - pk: Self::ProverKey, prover_state: &mut ProverState, - witnesses: Self::Witnesses, - instances: Self::Instances, - acc_instances: Self::AccumulatorInstances, - acc_witnesses: Self::AccumulatorWitnesses, - ) -> Result< - ( - (Self::AccumulatorInstances, Self::AccumulatorWitnesses), - Self::Proof, - ), - ProverError, - > { - debug_assert!(instances.len() > 1); - debug_assert_eq!(witnesses.len(), instances.len()); - debug_assert_eq!(acc_witnesses.0.len(), acc_instances.0.len()); - - let (l1, l) = (self.config.l1, self.config.l); - let l2 = l - l1; - debug_assert_eq!(l1 + l2, l); - - debug_assert!(l.is_power_of_two()); - - //////////////////////// - // 1. Parsing phase - //////////////////////// - // a. index - #[allow(non_snake_case)] - let (M, N, k) = (pk.1, pk.2, pk.3); - #[allow(non_snake_case)] - let (log_M, log_l) = (log2(M) as usize, log2(l) as usize); - - debug_assert_eq!(instances[0].len(), N - k); - - // b. and c. statements and accumulators - // d. absorb parameters - absorb_instances(prover_state, &instances); - absorb_accumulated_instances::(prover_state, &acc_instances); - - //////////////////////// - // 2. PESAT Reduction - //////////////////////// - let n = self.code.code_len(); - let log_n = log2(n) as usize; - + witnesses: &[Vec], + l1: usize, + #[allow(non_snake_case)] log_M: usize, + ) -> Result, ProverError> { // a. encode witnesses - let (codewords, leaves) = build_codeword_leaves(&self.code, &witnesses, l1); + let (codewords, leaves) = build_codeword_leaves(&self.params.code, witnesses, l1); // b. evaluation claims let mus = codewords.iter().map(|f| f[0]).collect::>(); // c. commit to witnesses let td_0 = MerkleTree::::new( - &self.mt_leaf_hash_params, - &self.mt_two_to_one_hash_params, + &self.params.mt_leaf_hash_params, + &self.params.mt_two_to_one_hash_params, leaves.chunks_exact(l1).collect::>(), )?; @@ -294,12 +215,51 @@ impl< .map(|_| prover_state.verifier_messages_vec::(log_M)) .collect::>(); - //////////////////////// - // 3. Constrained Code Accumulation - //////////////////////// + Ok(PesatOutput { + codewords, + td_0, + mus, + taus, + }) + } + + /// Phase 3: Constrained Code Accumulation + /// + /// Combines PESAT output with accumulated state, runs: + /// - Twin constraint sumcheck + /// - OOD sampling + /// - Batching sumcheck + /// - Authentication path computation + /// + /// Returns the new accumulator and proof. + #[allow(clippy::too_many_arguments)] + fn constrained_code_accumulate( + &self, + prover_state: &mut ProverState, + pesat: PesatOutput, + instances: &[Vec], + witnesses: &[Vec], + acc_instance: AccumulatorInstance, + acc_witness: AccumulatorWitness, + #[allow(non_snake_case)] M: usize, + #[allow(non_snake_case)] N: usize, + k: usize, + log_l: usize, + ) -> Result< + ( + (AccumulatorInstance, AccumulatorWitness), + WARPProof, + ), + ProverError, + > { + let n = self.params.code.code_len(); + let log_n = log2(n) as usize; + #[allow(non_snake_case)] + let log_M = log2(M) as usize; + let l1 = pesat.codewords.len(); + // a. zero check randomness let omega: F = prover_state.verifier_message(); - let tau = prover_state.verifier_messages_vec::(log_l); // b. define [...] @@ -308,30 +268,30 @@ impl< .map(|(index, _point)| eq_poly(&tau, index)) .collect::>(); - let alpha_vecs = concat_slices(&acc_instances.1, &vec![vec![F::zero(); log_n]; l1]); + let alpha_vecs = concat_slices(&acc_instance.alpha, &vec![vec![F::zero(); log_n]; l1]); // build the z (x, w) vectors - let z_vecs: Vec> = acc_instances - .3 - .1 + let z_vecs: Vec> = acc_instance + .beta + .1 .iter() - .zip(&acc_witnesses.2) - .chain(instances.iter().zip(&witnesses)) + .zip(&acc_witness.w) + .chain(instances.iter().zip(witnesses)) .map(|(x, w)| concat_slices(x, w)) .collect(); - let beta_vecs: Vec> = acc_instances.3 .0.into_iter().chain(taus).collect(); + let beta_vecs: Vec> = acc_instance.beta.0.into_iter().chain(pesat.taus).collect(); // Twin Constraint sumcheck let mut tablewise = [ - concat_slices(&acc_witnesses.1, &codewords), // u - z_vecs, // z - alpha_vecs, // a - beta_vecs, // b + concat_slices(&acc_witness.f, &pesat.codewords), // u + z_vecs, // z + alpha_vecs, // a + beta_vecs, // b ]; let mut pw = [tau_eq_evals]; // tau - let r1cs = self.p.constraints(); + let r1cs = self.params.p.constraints(); let expected_num_coeffs = 2 + (log_n + 1).max(log_M + 2); let sc = coefficient_sumcheck( |tw, pw| twin_constraint_round_poly(tw, pw, r1cs, omega, expected_num_coeffs), @@ -355,6 +315,7 @@ impl< let beta_eq_evals = (0..M).map(|i| eq_poly(&beta_tau, i)).collect::>(); let eta = self + .params .p .evaluate_bundled(&beta_eq_evals, &z) .map_err(|_| ProverError::SpongeFish)?; @@ -366,8 +327,8 @@ impl< // f. new commitment let td = MerkleTree::::new( - &self.mt_leaf_hash_params, - &self.mt_two_to_one_hash_params, + &self.params.mt_leaf_hash_params, + &self.params.mt_two_to_one_hash_params, f.chunks(1).collect::>(), )?; @@ -382,7 +343,7 @@ impl< prover_state.prover_message(&nu_0); // h. ood samples - let n_ood_samples = self.config.s * log_n; + let n_ood_samples = self.params.config.s * log_n; let ood_samples = prover_state.verifier_messages_vec::(n_ood_samples); let ood_samples = ood_samples.chunks(log_n).collect::>(); @@ -401,35 +362,19 @@ impl< nus.extend(ood_answers); // k. shift queries and zerocheck randomness - let r = 1 + self.config.s + self.config.t; + let r = 1 + self.params.config.s + self.params.config.t; let log_r = log2(r) as usize; - let n_shift_queries = (self.config.t * log_n).div_ceil(8); - let bytes_shift_queries = prover_state.verifier_messages_vec(n_shift_queries); + let queries = QueryIndices::sample(prover_state, log_n, self.params.config.t); let xis = prover_state.verifier_messages_vec(log_r); - // get shift queries as binary field elements - let binary_shift_queries = bytes_shift_queries - .iter() - .flat_map(byte_to_binary_field_array) - .take(self.config.t * log_n) - .collect::>(); - - let binary_shift_queries = binary_shift_queries.chunks(log_n).collect::>(); - - // build indexes out of the shift queries stored - let shift_queries_indexes: Vec = binary_shift_queries - .iter() - .map(|vals| binary_field_elements_to_usize(vals)) - .collect(); - - zetas.extend(binary_shift_queries); + zetas.extend(queries.evaluation_points.iter().map(|v| v.as_slice())); // l. sumcheck polynomials // compute evaluations for xi let xi_eq_evals = (0..r).map(|i| eq_poly(&xis, i)).collect::>(); - let ood_evals_vec = (0..1 + self.config.s) + let ood_evals_vec = (0..1 + self.params.config.s) .map(|i| { (0..n) .map(|a| eq_poly(zetas[i], a) * xi_eq_evals[i]) @@ -439,7 +384,7 @@ impl< // [CBBZ23] optimization from hyperplonk let id_non_0_eval_sums = - accumulate_sparse_evaluations(zetas, xi_eq_evals, self.config.s, r); + accumulate_sparse_evaluations(zetas, xi_eq_evals, self.params.config.s, r); // call efficient sumcheck for batched_constraint checks let alpha = inner_product_sumcheck( @@ -453,53 +398,148 @@ impl< let mu = f_hat.fix_variables(&alpha)[0]; // n. compute authentication paths - let auth_0 = compute_auth_paths(&td_0, &shift_queries_indexes)?; + let auth_0 = compute_auth_paths(&pesat.td_0, &queries.leaf_positions)?; - let auth = acc_witnesses - .0 // for each accumulated witness and for each + let auth = acc_witness + .td // for each accumulated witness and for each .iter() - .map(|td| compute_auth_paths(td, &shift_queries_indexes)) + .map(|td| compute_auth_paths(td, &queries.leaf_positions)) .collect::>>, Error>>()?; - let all_codewords = acc_witnesses - .1 + let all_codewords = acc_witness + .f .into_iter() - .chain(codewords) + .chain(pesat.codewords) .collect::>(); let mut shift_queries_answers = - vec![vec![F::default(); all_codewords.len()]; shift_queries_indexes.len()]; - for (i, idx) in shift_queries_indexes.iter().enumerate() { + vec![vec![F::default(); all_codewords.len()]; queries.leaf_positions.len()]; + for (i, idx) in queries.leaf_positions.iter().enumerate() { let answers = all_codewords.iter().map(|f| f[*idx]).collect::>(); shift_queries_answers[i] = answers; } - let acc_instance = (vec![td.root()], vec![alpha], vec![mu], beta, vec![eta]); - let acc_witness = (vec![td], vec![f], vec![w.to_vec()]); - - // 4. return - Ok(( - (acc_instance, acc_witness), - ( - td_0.root(), - mus, - nu_0, - nus, - auth_0, - auth, - shift_queries_answers, - ), - )) + let new_acc_instance = AccumulatorInstance { + rt: vec![td.root()], + alpha: vec![alpha], + mu: vec![mu], + beta, + eta: vec![eta], + }; + let new_acc_witness = AccumulatorWitness { + td: vec![td], + f: vec![f], + w: vec![w.to_vec()], + }; + + let proof = WARPProof { + rt_0: pesat.td_0.root(), + mu_i: pesat.mus, + nu_0, + nu_i: nus, + auth_0, + auth_j: auth, + shift_query_answers: shift_queries_answers, + }; + + Ok(((new_acc_instance, new_acc_witness), proof)) + } +} + +impl< + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + P: Clone + BundledPESAT, Config = (usize, usize, usize)>, // m, n, k + C: LinearCode + Clone, + MT: Config + From<[u8; 32]>>, + > AccumulationScheme for WARP +{ + type Index = P; + type ProverKey = (P, usize, usize, usize); + type VerifierKey = (usize, usize, usize); + type Instances = Vec>; + type Witnesses = Vec>; + + fn index( + prover_state: &mut ProverState, + index: Self::Index, + ) -> spongefish::VerificationResult<(Self::ProverKey, Self::VerifierKey)> { + let (m, n, k) = index.config(); + // initialize prover state for fs + // TODO for R1CS + prover_state.public_message(&index.description()); + prover_state.prover_message(&F::from(m as u32)); + prover_state.prover_message(&F::from(n as u32)); + prover_state.prover_message(&F::from(k as u32)); + Ok(((index, m, n, k), (m, n, k))) + } + + fn prove( + &self, + pk: Self::ProverKey, + prover_state: &mut ProverState, + witnesses: Self::Witnesses, + instances: Self::Instances, + acc_instance: AccumulatorInstance, + acc_witness: AccumulatorWitness, + ) -> Result< + ( + (AccumulatorInstance, AccumulatorWitness), + WARPProof, + ), + ProverError, + > { + debug_assert!(instances.len() > 1); + debug_assert_eq!(witnesses.len(), instances.len()); + debug_assert_eq!(acc_witness.td.len(), acc_instance.rt.len()); + + let (l1, l) = (self.params.config.l1, self.params.config.l); + let l2 = l - l1; + debug_assert_eq!(l1 + l2, l); + debug_assert!(l.is_power_of_two()); + + //////////////////////// + // 1. Parsing phase + //////////////////////// + #[allow(non_snake_case)] + let (M, N, k) = (pk.1, pk.2, pk.3); + #[allow(non_snake_case)] + let (log_M, log_l) = (log2(M) as usize, log2(l) as usize); + + debug_assert_eq!(instances[0].len(), N - k); + + absorb_instances(prover_state, &instances); + absorb_accumulated_instances::(prover_state, &acc_instance); + + //////////////////////// + // 2. PESAT Reduction + //////////////////////// + let pesat = self.pesat_reduce(prover_state, &witnesses, l1, log_M)?; + + //////////////////////// + // 3. Constrained Code Accumulation + //////////////////////// + self.constrained_code_accumulate( + prover_state, + pesat, + &instances, + &witnesses, + acc_instance, + acc_witness, + M, + N, + k, + log_l, + ) } fn verify<'a>( &self, vk: Self::VerifierKey, verifier_state: &mut VerifierState<'a>, - acc_instance: Self::AccumulatorInstances, - proof: Self::Proof, + acc_instance: AccumulatorInstance, + proof: WARPProof, ) -> Result<(), VerifierError> { - let (l1, l) = (self.config.l1, self.config.l); + let (l1, l) = (self.params.config.l1, self.params.config.l); let l2 = l - l1; //////////////////////// @@ -511,12 +551,20 @@ impl< #[allow(non_snake_case)] let (log_M, log_l) = (log2(M) as usize, log2(l) as usize); - let n = self.code.code_len(); + let n = self.params.code.code_len(); let log_n = log2(n) as usize; // f. absorb parameters - let (l1_xs, (l2_roots, l2_alphas, l2_mus, (l2_taus, l2_xs), l2_etas)) = - parse_statement::(verifier_state, l1, l2, N - k, log_n, log_M)?; + let ( + l1_xs, + AccumulatorInstance { + rt: l2_roots, + alpha: l2_alphas, + mu: l2_mus, + beta: (l2_taus, l2_xs), + eta: l2_etas, + }, + ) = parse_statement::(verifier_state, l1, l2, N - k, log_n, log_M)?; //////////////////////// // 2. Derive randomness @@ -542,12 +590,12 @@ impl< l1, log_n, log_l, - self.config.s, - self.config.t, + self.params.config.s, + self.params.config.t, log_M, )?; - let r = 1 + self.config.s + self.config.t; + let r = 1 + self.params.config.s + self.params.config.t; let log_r = log2(r) as usize; //////////////////////// @@ -561,8 +609,8 @@ impl< let zeta_0 = scale_and_sum(&alpha_vecs, &gamma_eq_evals); // compute \eta_{s + k} - let mut nu_s_t = vec![F::default(); self.config.t]; - for (i, v_jk) in proof.6.iter().enumerate() { + let mut nu_s_t = vec![F::default(); self.params.config.t]; + for (i, v_jk) in proof.shift_query_answers.iter().enumerate() { let res = v_jk .iter() .zip(&gamma_eq_evals) @@ -596,7 +644,7 @@ impl< // 4. Decision phase //////////////////////// // a. new code evaluation point - (acc_instance.1[0] == alpha_sumcheck).ok_or_err(VerifierError::CodeEvaluationPoint)?; + (acc_instance.alpha[0] == alpha_sumcheck).ok_or_err(VerifierError::CodeEvaluationPoint)?; // b. new circuit evaluation point let betas = l2_taus @@ -606,55 +654,46 @@ impl< .map(|(tau, x)| concat_slices(&tau, &x)) .collect::>>(); let beta = scale_and_sum(&betas, &gamma_eq_evals); - let expected_beta = concat_slices(&acc_instance.3 .0[0], &acc_instance.3 .1[0]); + let expected_beta = concat_slices(&acc_instance.beta.0[0], &acc_instance.beta.1[0]); (expected_beta == beta).ok_or_err(VerifierError::CircuitEvaluationPoint)?; // c. check auth paths - let binary_shift_queries = bytes_shift_queries - .iter() - .flat_map(byte_to_binary_field_array) - .take(self.config.t * log_n) - .collect::>(); - - let binary_shift_queries = binary_shift_queries.chunks(log_n).collect::>(); - - let shift_queries_indexes: Vec = binary_shift_queries - .iter() - .map(|vals| binary_field_elements_to_usize(vals)) - .collect(); + let queries: QueryIndices = + QueryIndices::from_squeezed_bytes(&bytes_shift_queries, log_n, self.params.config.t); // check: // that the leaf index corresponds to the shift query // that the path is correct - (proof.6.len() == self.config.t).ok_or_err(VerifierError::NumShiftQueries)?; + (proof.shift_query_answers.len() == self.params.config.t) + .ok_or_err(VerifierError::NumShiftQueries)?; - // proof.4 is auth_0 - for (i, path) in proof.4.iter().enumerate() { - (path.leaf_index == shift_queries_indexes[i]) + // proof.auth_0 + for (i, path) in proof.auth_0.iter().enumerate() { + (path.leaf_index == queries.leaf_positions[i]) .ok_or_err(VerifierError::ShiftQueryIndex)?; let is_valid = path.verify( - &self.mt_leaf_hash_params, - &self.mt_two_to_one_hash_params, + &self.params.mt_leaf_hash_params, + &self.params.mt_two_to_one_hash_params, &rt_0, - &proof.6[i][l2..], // leaves are evaluations of the l1 codewords + &proof.shift_query_answers[i][l2..], // leaves are evaluations of the l1 codewords )?; is_valid.ok_or_err(VerifierError::ShiftQuery)? } - // proof.5 holds merkle proofs for l2 accumulated instances - (proof.5.len() == l2).ok_or_err(VerifierError::NumL2Instances)?; - for (i, paths) in proof.5.iter().enumerate() { - (paths.len() == self.config.t).ok_or_err(VerifierError::NumShiftQueries)?; + // proof.auth_j holds merkle proofs for l2 accumulated instances + (proof.auth_j.len() == l2).ok_or_err(VerifierError::NumL2Instances)?; + for (i, paths) in proof.auth_j.iter().enumerate() { + (paths.len() == self.params.config.t).ok_or_err(VerifierError::NumShiftQueries)?; let root = &l2_roots[i]; for (j, path) in paths.iter().enumerate() { - (path.leaf_index == shift_queries_indexes[j]) + (path.leaf_index == queries.leaf_positions[j]) .ok_or_err(VerifierError::ShiftQueryIndex)?; let is_valid = path.verify( - &self.mt_leaf_hash_params, - &self.mt_two_to_one_hash_params, + &self.params.mt_leaf_hash_params, + &self.params.mt_two_to_one_hash_params, root, - [proof.6[j][i]], // proof.6[j][i] holds f_i(x_j) + [proof.shift_query_answers[j][i]], // shift_query_answers[j][i] holds f_i(x_j) )?; is_valid.ok_or_err(VerifierError::ShiftQuery)? @@ -699,7 +738,8 @@ impl< .collect::>(), ); zeta_eqs.extend( - binary_shift_queries + queries + .evaluation_points .iter() .map(|zeta| eq_poly_non_binary(zeta, &alpha_sumcheck)) .collect::>(), @@ -707,7 +747,7 @@ impl< (zeta_eqs.len() == r).ok_or_err(VerifierError::NumShiftQueries)?; // mul by \mu and compare to target_2 - (acc_instance.2[0] + (acc_instance.mu[0] * zeta_eqs .into_iter() .zip(xi_eq_evals) @@ -720,39 +760,41 @@ impl< fn decide( &self, - acc_witness: Self::AccumulatorWitnesses, - acc_instance: Self::AccumulatorInstances, + acc_witness: AccumulatorWitness, + acc_instance: AccumulatorInstance, ) -> Result<(), WARPError> { - let (mt, f, w) = acc_witness; - let (rt, alpha, mu, beta, eta) = acc_instance; - let computed_mt = MerkleTree::::new( - &self.mt_leaf_hash_params, - &self.mt_two_to_one_hash_params, - f[0].chunks(1).collect::>(), + &self.params.mt_leaf_hash_params, + &self.params.mt_two_to_one_hash_params, + acc_witness.f[0].chunks(1).collect::>(), )?; - (rt[0] == computed_mt.root()).ok_or_err(DeciderError::MerkleRoot)?; - (mt[0].root() == computed_mt.root()).ok_or_err(DeciderError::MerkleTrapDoor)?; + (acc_instance.rt[0] == computed_mt.root()).ok_or_err(DeciderError::MerkleRoot)?; + (acc_witness.td[0].root() == computed_mt.root()).ok_or_err(DeciderError::MerkleTrapDoor)?; let f_hat = DenseMultilinearExtension::from_evaluations_slice( - log2(self.code.code_len()) as usize, - &f[0], + log2(self.params.code.code_len()) as usize, + &acc_witness.f[0], ); - (f_hat.evaluate(&alpha[0]) == mu[0]).ok_or_err(DeciderError::MLExtensionEvaluation)?; + (f_hat.evaluate(&acc_instance.alpha[0]) == acc_instance.mu[0]) + .ok_or_err(DeciderError::MLExtensionEvaluation)?; - let tau = &beta.0[0]; + let tau = &acc_instance.beta.0[0]; let tau_zero_evader = Hypercube::::new(tau.len()) .map(|(index, _point)| eq_poly(tau, index)) .collect::>(); - let mut z = beta.1[0].clone(); - z.extend(w[0].clone()); - let computed_eta = self.p.evaluate_bundled(&tau_zero_evader, &z).unwrap(); - (computed_eta == eta[0]).ok_or_err(DeciderError::BundledEvaluation)?; + let mut z = acc_instance.beta.1[0].clone(); + z.extend(acc_witness.w[0].clone()); + let computed_eta = self + .params + .p + .evaluate_bundled(&tau_zero_evader, &z) + .unwrap(); + (computed_eta == acc_instance.eta[0]).ok_or_err(DeciderError::BundledEvaluation)?; - let computed_f = self.code.encode(&w[0]); - (f[0] == computed_f).ok_or_err(DeciderError::EncodedWitness)?; + let computed_f = self.params.code.encode(&acc_witness.w[0]); + (acc_witness.f[0] == computed_f).ok_or_err(DeciderError::EncodedWitness)?; Ok(()) } @@ -763,6 +805,7 @@ pub mod test { use super::crypto::merkle::blake3::Blake3MerkleTreeParams; use super::AccumulationScheme; use crate::serialize::{AccInstanceSerializer, AccWitnessSerializer, ProofSerializer}; + use crate::types::{AccumulatorInstance, AccumulatorWitness}; use crate::{ relations::{ r1cs::{ @@ -859,20 +902,20 @@ pub mod test { &mut prover_state, instances_witnesses.1.clone(), instances_witnesses.0.clone(), - (vec![], vec![], vec![], (vec![], vec![]), vec![]), - (vec![], vec![], vec![]), + AccumulatorInstance::empty(), + AccumulatorWitness::empty(), ) .unwrap(); - acc_roots.push(acc_x.0[0].clone()); - acc_alphas.push(acc_x.1[0].clone()); - acc_mus.push(acc_x.2[0]); - acc_taus.push(acc_x.3 .0[0].clone()); - acc_xs.push(acc_x.3 .1[0].clone()); - acc_eta.push(acc_x.4[0]); - - acc_tds.push(acc_w.0[0].clone()); - acc_f.push(acc_w.1[0].clone()); - acc_ws.push(acc_w.2[0].clone()); + acc_roots.push(acc_x.rt[0].clone()); + acc_alphas.push(acc_x.alpha[0].clone()); + acc_mus.push(acc_x.mu[0]); + acc_taus.push(acc_x.beta.0[0].clone()); + acc_xs.push(acc_x.beta.1[0].clone()); + acc_eta.push(acc_x.eta[0]); + + acc_tds.push(acc_w.td[0].clone()); + acc_f.push(acc_w.f[0].clone()); + acc_ws.push(acc_w.w[0].clone()); } let domainsep = spongefish::domain_separator!("test::warp"); @@ -895,8 +938,18 @@ pub mod test { &mut prover_state, instances_witnesses.1, instances_witnesses.0, - (acc_roots, acc_alphas, acc_mus, (acc_taus, acc_xs), acc_eta), - (acc_tds, acc_f, acc_ws), + AccumulatorInstance { + rt: acc_roots, + alpha: acc_alphas, + mu: acc_mus, + beta: (acc_taus, acc_xs), + eta: acc_eta, + }, + AccumulatorWitness { + td: acc_tds, + f: acc_f, + w: acc_ws, + }, ) .unwrap(); @@ -1007,20 +1060,20 @@ pub mod test { &mut prover_state, instances_witnesses.1.clone(), instances_witnesses.0.clone(), - (vec![], vec![], vec![], (vec![], vec![]), vec![]), - (vec![], vec![], vec![]), + AccumulatorInstance::empty(), + AccumulatorWitness::empty(), ) .unwrap(); - acc_roots.push(acc_x.0[0].clone()); - acc_alphas.push(acc_x.1[0].clone()); - acc_mus.push(acc_x.2[0]); - acc_taus.push(acc_x.3 .0[0].clone()); - acc_xs.push(acc_x.3 .1[0].clone()); - acc_eta.push(acc_x.4[0]); - - acc_tds.push(acc_w.0[0].clone()); - acc_f.push(acc_w.1[0].clone()); - acc_ws.push(acc_w.2[0].clone()); + acc_roots.push(acc_x.rt[0].clone()); + acc_alphas.push(acc_x.alpha[0].clone()); + acc_mus.push(acc_x.mu[0]); + acc_taus.push(acc_x.beta.0[0].clone()); + acc_xs.push(acc_x.beta.1[0].clone()); + acc_eta.push(acc_x.eta[0]); + + acc_tds.push(acc_w.td[0].clone()); + acc_f.push(acc_w.f[0].clone()); + acc_ws.push(acc_w.w[0].clone()); } let domainsep = spongefish::domain_separator!("test::warp"); @@ -1044,8 +1097,18 @@ pub mod test { &mut prover_state, instances_witnesses.1, instances_witnesses.0, - (acc_roots, acc_alphas, acc_mus, (acc_taus, acc_xs), acc_eta), - (acc_tds, acc_f, acc_ws), + AccumulatorInstance { + rt: acc_roots, + alpha: acc_alphas, + mu: acc_mus, + beta: (acc_taus, acc_xs), + eta: acc_eta, + }, + AccumulatorWitness { + td: acc_tds, + f: acc_f, + w: acc_ws, + }, ) .unwrap(); diff --git a/src/protocol/domainsep/mod.rs b/src/protocol/domainsep/mod.rs index 4bd0d1f..a3dd928 100644 --- a/src/protocol/domainsep/mod.rs +++ b/src/protocol/domainsep/mod.rs @@ -6,13 +6,7 @@ use spongefish::{ Decoding, Encoding, NargDeserialize, ProverState, VerificationResult, VerifierState, }; -pub type AccInstances = ( - Vec<::InnerDigest>, // rt - Vec>, // alpha - Vec, // mu - (Vec>, Vec>), // (tau, x) - Vec, // eta -); +use crate::types::AccumulatorInstance; pub fn absorb_instances>( prover_state: &mut ProverState, @@ -30,47 +24,47 @@ pub fn absorb_accumulated_instances< MT: Config + From<[u8; 32]>>, >( prover_state: &mut ProverState, - acc_instances: &AccInstances, + acc_instance: &AccumulatorInstance, ) { // digests (rt) - for digest in &acc_instances.0 { + for digest in &acc_instance.rt { let bytes: [u8; 32] = digest.as_ref().try_into().expect("digest must be 32 bytes"); prover_state.prover_message(&bytes); } // alpha - for alpha in &acc_instances.1 { + for alpha in &acc_instance.alpha { for f in alpha { prover_state.prover_message(f); } } // mu - for f in &acc_instances.2 { + for f in &acc_instance.mu { prover_state.prover_message(f); } // taus - for tau in &acc_instances.3 .0 { + for tau in &acc_instance.beta.0 { for f in tau { prover_state.prover_message(f); } } // xs - for x in &acc_instances.3 .1 { + for x in &acc_instance.beta.1 { for f in x { prover_state.prover_message(f); } } // etas - for f in &acc_instances.4 { + for f in &acc_instance.eta { prover_state.prover_message(f); } } -pub type ParsedStatement = (Vec>, AccInstances); +pub type ParsedStatement = (Vec>, AccumulatorInstance); pub fn parse_statement< F: Field + NargDeserialize + Encoding<[u8]> + Decoding<[u8]>, @@ -121,7 +115,13 @@ pub fn parse_statement< Ok(( l1_xs, - (l2_roots, l2_alphas, l2_mus, (l2_taus, l2_xs), l2_etas), + AccumulatorInstance { + rt: l2_roots, + alpha: l2_alphas, + mu: l2_mus, + beta: (l2_taus, l2_xs), + eta: l2_etas, + }, )) } diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 6aa899d..d7fa0fa 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1 +1,2 @@ pub mod domainsep; +pub mod query; diff --git a/src/protocol/query.rs b/src/protocol/query.rs new file mode 100644 index 0000000..c727d93 --- /dev/null +++ b/src/protocol/query.rs @@ -0,0 +1,182 @@ +use ark_ff::Field; +use spongefish::ProverState; + +pub struct QueryIndices { + pub leaf_positions: Vec, // for merkle tree lookups + pub evaluation_points: Vec>, // for eq polynomial evals +} + +impl QueryIndices { + // take the prover state and sample for queries + pub fn sample( + prover_state: &mut ProverState, + log_codeword_len: usize, + num_queries: usize, + ) -> Self { + let num_bytes = (num_queries * log_codeword_len).div_ceil(8); + let squeezed_bytes: Vec = prover_state + .verifier_messages_vec::<[u8; 1]>(num_bytes) + .into_iter() + .map(|[b]| b) + .collect(); + Self::from_squeezed_bytes(&squeezed_bytes, log_codeword_len, num_queries) + } + + // format the queries from squeezed bytes + pub fn from_squeezed_bytes(squeezed_bytes: &[u8], log_n: usize, count: usize) -> Self { + let evaluation_points = Self::evaluation_points_from_squeezed_bytes(squeezed_bytes, log_n, count); + // Compute all leaf positions in one batch + let leaf_positions = Self::leaf_positions_from_evaluation_points(&evaluation_points); + Self { + leaf_positions, + evaluation_points, + } + } + + // Get Vec of len=num_queries, where each elements is vec of F in {0,1} len=log_codeword_len + fn evaluation_points_from_squeezed_bytes( + squeezed_bytes: &[u8], + log_codeword_len: usize, + num_queries: usize, + ) -> Vec> { + squeezed_bytes + .iter() + .flat_map(|squeezed_byte| (0..8).map(move |i| F::from((squeezed_byte >> i) & 1 == 1))) + .take(num_queries * log_codeword_len) + .collect::>() + .chunks(log_codeword_len) + .map(|chunk| chunk.to_vec()) + .collect() + } + + // Convert each evaluation point (vector of F in {0,1}) to its little-endian leaf index. + fn leaf_positions_from_evaluation_points(evaluation_points: &[Vec]) -> Vec { + let binary_to_leaf_index = |bits: &Vec| -> usize { + bits.iter() + .rev() + .fold(0, |acc, &b| (acc << 1) | b.is_one() as usize) + }; + evaluation_points.iter().map(binary_to_leaf_index).collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ark_bls12_381::Fr as BLS12_381; + use ark_std::{One, Zero}; + use crate::utils::fields::Goldilocks; + + // check dimensions, binary values, and leaf position range + fn check_query_indices(bytes: &[u8], log_n: usize, num_queries: usize) { + let q = QueryIndices::::from_squeezed_bytes(bytes, log_n, num_queries); + + assert_eq!(q.leaf_positions.len(), num_queries); + assert_eq!(q.evaluation_points.len(), num_queries); + + for (i, eval_pt) in q.evaluation_points.iter().enumerate() { + assert_eq!(eval_pt.len(), log_n); + for &bit in eval_pt { + assert!(bit.is_zero() || bit.is_one()); + } + assert!(q.leaf_positions[i] < (1 << log_n)); + } + } + + // check leaf_positions match manual binary-to-index conversion + fn check_roundtrip(bytes: &[u8], log_n: usize, num_queries: usize) { + let q = QueryIndices::::from_squeezed_bytes(bytes, log_n, num_queries); + for (i, eval_pt) in q.evaluation_points.iter().enumerate() { + let expected = eval_pt + .iter() + .rev() + .fold(0usize, |acc, &b| (acc << 1) | b.is_one() as usize); + assert_eq!(q.leaf_positions[i], expected); + } + } + + // BLS12-381 (multi-limb, 256-bit) + + #[test] + fn bls12_381_basic() { + let bytes = vec![0b10110010, 0b01101001, 0b11110000, 0b00001111]; + check_query_indices::(&bytes, 4, 3); + } + + #[test] + fn bls12_381_roundtrip() { + let bytes: Vec = (0..16).collect(); + check_roundtrip::(&bytes, 8, 10); + } + + #[test] + fn bls12_381_single_bit_queries() { + // log_n = 1 → each query is a single bit + let bytes = vec![0b10101010]; + let q = QueryIndices::::from_squeezed_bytes(&bytes, 1, 8); + assert_eq!(q.leaf_positions.len(), 8); + for &pos in &q.leaf_positions { + assert!(pos <= 1); + } + } + + // Goldilocks (SmallFp, single-limb u128) + + #[test] + fn goldilocks_basic() { + let bytes = vec![0xFF, 0x00, 0xAB, 0xCD]; + check_query_indices::(&bytes, 4, 3); + } + + #[test] + fn goldilocks_roundtrip() { + let bytes: Vec = (0..16).collect(); + check_roundtrip::(&bytes, 8, 10); + } + + #[test] + fn goldilocks_large_log_n() { + // 16-bit queries → range [0, 65536) + let bytes: Vec = (0..=255).cycle().take(64).collect(); + check_query_indices::(&bytes, 16, 4); + } + + // edge cases + + #[test] + fn zero_bytes_produce_zero_indices() { + let bytes = vec![0u8; 8]; + let q = QueryIndices::::from_squeezed_bytes(&bytes, 4, 4); + for &pos in &q.leaf_positions { + assert_eq!(pos, 0); + } + for eval_pt in &q.evaluation_points { + for &bit in eval_pt { + assert!(bit.is_zero()); + } + } + } + + #[test] + fn all_ones_bytes() { + let bytes = vec![0xFF; 8]; + let q = QueryIndices::::from_squeezed_bytes(&bytes, 4, 4); + for &pos in &q.leaf_positions { + assert_eq!(pos, (1 << 4) - 1); + } + for eval_pt in &q.evaluation_points { + for &bit in eval_pt { + assert!(bit.is_one()); + } + } + } + + #[test] + fn deterministic_output() { + let bytes = vec![0x42, 0x13, 0x7F, 0xE0]; + let q1 = QueryIndices::::from_squeezed_bytes(&bytes, 4, 4); + let q2 = QueryIndices::::from_squeezed_bytes(&bytes, 4, 4); + assert_eq!(q1.leaf_positions, q2.leaf_positions); + assert_eq!(q1.evaluation_points, q2.evaluation_points); + } +} diff --git a/src/serialize.rs b/src/serialize.rs index 5d5bb0d..84d159a 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -1,28 +1,8 @@ -use ark_crypto_primitives::merkle_tree::{Config, MerkleTree, Path}; +use ark_crypto_primitives::merkle_tree::{Config, Path}; use ark_ff::{Field, PrimeField}; use ark_serialize::CanonicalSerialize; -pub type AccWitnessTuple = (Vec>, Vec>, Vec>); - -#[allow(clippy::type_complexity)] -pub type AccInstanceTuple = ( - Vec<::InnerDigest>, - Vec>, - Vec, - (Vec>, Vec>), - Vec, -); - -#[allow(clippy::type_complexity)] -pub type ProofTuple = ( - ::InnerDigest, - Vec, - F, - Vec, - Vec>, - Vec>>, - Vec>, -); +use crate::types::{AccumulatorInstance, AccumulatorWitness, WARPProof}; #[derive(CanonicalSerialize)] pub struct AccWitnessSerializer< @@ -37,13 +17,13 @@ pub struct AccWitnessSerializer< impl + From<[u8; 32]>>> AccWitnessSerializer { - pub fn new(acc_witness: AccWitnessTuple) -> Self { - assert_eq!(acc_witness.0.len(), 1); - assert_eq!(acc_witness.1.len(), 1); - assert_eq!(acc_witness.2.len(), 1); + pub fn new(acc_witness: AccumulatorWitness) -> Self { + assert_eq!(acc_witness.td.len(), 1); + assert_eq!(acc_witness.f.len(), 1); + assert_eq!(acc_witness.w.len(), 1); Self { - f: acc_witness.1[0].clone(), - w: acc_witness.2[0].clone(), + f: acc_witness.f.into_iter().next().unwrap(), + w: acc_witness.w.into_iter().next().unwrap(), _mt: std::marker::PhantomData, } } @@ -64,20 +44,23 @@ pub struct AccInstanceSerializer< impl + From<[u8; 32]>>> AccInstanceSerializer { - pub fn new(acc_instance: AccInstanceTuple) -> Self { - assert_eq!(acc_instance.0.len(), 1); - assert_eq!(acc_instance.1.len(), 1); - assert_eq!(acc_instance.2.len(), 1); - assert_eq!(acc_instance.3 .0.len(), 1); - assert_eq!(acc_instance.3 .1.len(), 1); - assert_eq!(acc_instance.4.len(), 1); - let beta = (acc_instance.3 .0[0].clone(), acc_instance.3 .1[0].clone()); + pub fn new(acc_instance: AccumulatorInstance) -> Self { + assert_eq!(acc_instance.rt.len(), 1); + assert_eq!(acc_instance.alpha.len(), 1); + assert_eq!(acc_instance.mu.len(), 1); + assert_eq!(acc_instance.beta.0.len(), 1); + assert_eq!(acc_instance.beta.1.len(), 1); + assert_eq!(acc_instance.eta.len(), 1); + let beta = ( + acc_instance.beta.0.into_iter().next().unwrap(), + acc_instance.beta.1.into_iter().next().unwrap(), + ); Self { - rt: acc_instance.0[0].clone(), - alpha: acc_instance.1[0].clone(), - mu: acc_instance.2[0], + rt: acc_instance.rt.into_iter().next().unwrap(), + alpha: acc_instance.alpha.into_iter().next().unwrap(), + mu: acc_instance.mu[0], beta, - eta: acc_instance.4[0], + eta: acc_instance.eta[0], } } } @@ -99,15 +82,15 @@ pub struct ProofSerializer< impl + From<[u8; 32]>>> ProofSerializer { - pub fn new(proof: ProofTuple) -> Self { + pub fn new(proof: WARPProof) -> Self { Self { - rt_0: proof.0, - mu_i: proof.1, - nu_0: proof.2, - nu_i: proof.3, - auth_0: proof.4, - auth_j: proof.5, - f_i_x_j: proof.6, + rt_0: proof.rt_0, + mu_i: proof.mu_i, + nu_0: proof.nu_0, + nu_i: proof.nu_i, + auth_0: proof.auth_0, + auth_j: proof.auth_j, + f_i_x_j: proof.shift_query_answers, } } } diff --git a/src/traits.rs b/src/traits.rs index 0bc6925..e5b3bd9 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -3,24 +3,14 @@ use ark_ff::Field; use spongefish::{ProverState, VerificationResult, VerifierState}; use crate::error::{ProverError, VerifierError, WARPError}; - -pub type WARPAccumResult = ( - ( - >::AccumulatorInstances, - >::AccumulatorWitnesses, - ), - >::Proof, -); +use crate::types::{AccumulatorInstance, AccumulatorWitness, WARPProof}; pub trait AccumulationScheme { type Index; type ProverKey; type VerifierKey; - type AccumulatorInstances; - type AccumulatorWitnesses; type Instances; type Witnesses; - type Proof; // on given index, returns prover and verifier keys fn index( @@ -35,21 +25,27 @@ pub trait AccumulationScheme { prover_state: &mut ProverState, witnesses: Self::Witnesses, instances: Self::Instances, - acc_instances: Self::AccumulatorInstances, - acc_witnesses: Self::AccumulatorWitnesses, - ) -> Result, ProverError>; + acc_instance: AccumulatorInstance, + acc_witness: AccumulatorWitness, + ) -> Result< + ( + (AccumulatorInstance, AccumulatorWitness), + WARPProof, + ), + ProverError, + >; fn verify<'a>( &self, vk: Self::VerifierKey, verifier_state: &mut VerifierState<'a>, - acc_instance: Self::AccumulatorInstances, - proof: Self::Proof, + acc_instance: AccumulatorInstance, + proof: WARPProof, ) -> Result<(), VerifierError>; fn decide( &self, - acc_witness: Self::AccumulatorWitnesses, - acc_instance: Self::AccumulatorInstances, + acc_witness: AccumulatorWitness, + acc_instance: AccumulatorInstance, ) -> Result<(), WARPError>; } diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..6434bb6 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,106 @@ +use ark_codes::traits::LinearCode; +use ark_crypto_primitives::{ + crh::{CRHScheme, TwoToOneCRHScheme}, + merkle_tree::{Config, MerkleTree, Path}, +}; +use ark_ff::Field; +use std::marker::PhantomData; + +use crate::config::WARPConfig; +use crate::relations::BundledPESAT; + +/// Protocol parameters for WARP — the shared configuration used by all IOR phases. +pub struct WARPParams, C: LinearCode + Clone, MT: Config> { + pub _f: PhantomData, + pub config: WARPConfig, + pub code: C, + pub p: P, + pub mt_leaf_hash_params: ::Parameters, + pub mt_two_to_one_hash_params: ::Parameters, +} +/// Accumulator instance — the public part of an accumulated claim. +/// +/// Corresponds to `(rt, α, μ, (τ, x), η)` in the paper. +#[derive(Clone)] +pub struct AccumulatorInstance { + /// Merkle tree root commitments. + pub rt: Vec, + /// Code evaluation points (one per accumulated oracle). + pub alpha: Vec>, + /// Code evaluation targets (one per accumulated oracle). + pub mu: Vec, + /// Circuit evaluation points: `(τ_i, x_i)` pairs. + pub beta: (Vec>, Vec>), + /// Bundled PESAT evaluation targets. + pub eta: Vec, +} + +impl AccumulatorInstance { + pub fn empty() -> Self { + Self { + rt: vec![], + alpha: vec![], + mu: vec![], + beta: (vec![], vec![]), + eta: vec![], + } + } +} + +/// Accumulator witness — the private part of an accumulated claim. +/// +/// Corresponds to `(td, f, w)` in the paper. +#[derive(Clone)] +pub struct AccumulatorWitness { + /// Merkle tree trapdoors (full trees). + pub td: Vec>, + /// Oracle evaluations (codewords). + pub f: Vec>, + /// R1CS witnesses. + pub w: Vec>, +} + +impl AccumulatorWitness { + pub fn empty() -> Self { + Self { + td: vec![], + f: vec![], + w: vec![], + } + } +} + +/// Proof produced by the WARP accumulation prover. +/// +/// Corresponds to `(rt₀, μᵢ, ν₀, νᵢ, auth₀, authⱼ, f_i(x_j))` in the paper. +#[derive(Clone)] +pub struct WARPProof { + /// Fresh Merkle tree root. + pub rt_0: MT::InnerDigest, + /// Fresh code evaluations at 0. + pub mu_i: Vec, + /// Evaluation of accumulated oracle at zeta_0. + pub nu_0: F, + /// Evaluation claims (OOD + shift query answers). + pub nu_i: Vec, + /// Authentication paths for the fresh commitment. + pub auth_0: Vec>, + /// Authentication paths for each accumulated commitment. + pub auth_j: Vec>>, + /// Shift query answers: `f_i(x_j)` for each query position `j` and oracle `i`. + pub shift_query_answers: Vec>, +} + +/// Intermediate output of the PESAT reduction phase. +/// +/// This data flows from Phase 2 (PESAT Reduction) into Phase 3 (Constrained Code Accumulation). +pub(crate) struct PesatOutput { + /// Encoded codewords from fresh witnesses. + pub codewords: Vec>, + /// Merkle tree over the interleaved codeword leaves. + pub td_0: MerkleTree, + /// Code evaluation claims: `f_i(0)` for each codeword. + pub mus: Vec, + /// PESAT evaluation challenges (one per fresh instance). + pub taus: Vec>, +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index eb85d34..644cb1b 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -12,30 +12,6 @@ pub fn chunk_size() -> usize { chunk_size_bytes(F::MODULUS_BIT_SIZE) } -pub fn bytes_to_vec_f(bytes: &[u8]) -> Vec { - bytes - .chunks(chunk_size::()) - .map(|chunk| F::from_le_bytes_mod_order(chunk)) - .collect() -} - -pub fn byte_to_binary_field_array(byte: &u8) -> Vec { - (0..8) - .map(|i| { - let val = (byte >> i) & 1 == 1; - // return in field element and in binary - F::from(val) - }) - .collect::>() -} - -pub fn binary_field_elements_to_usize(elements: &[F]) -> usize { - elements - .iter() - .rev() - .fold(0, |acc, &b| (acc << 1) | b.is_one() as usize) -} - pub fn concat_slices(a: &[F], b: &[F]) -> Vec { let mut v = Vec::::with_capacity(a.len() + b.len()); v.extend_from_slice(a); From 6f836e1f0fce8c2912e1b4ae859a2ee9852b4e7a Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Wed, 25 Mar 2026 18:30:43 +0100 Subject: [PATCH 03/68] clippy --- src/ior.rs | 61 ------- src/lib.rs | 49 +++--- src/protocol/domainsep/mod.rs | 247 ---------------------------- src/protocol/mod.rs | 2 +- src/protocol/query.rs | 5 +- src/protocol/transcript/mod.rs | 5 + src/protocol/transcript/prover.rs | 57 +++++++ src/protocol/transcript/verifier.rs | 199 ++++++++++++++++++++++ src/traits.rs | 12 +- src/types.rs | 10 ++ 10 files changed, 299 insertions(+), 348 deletions(-) delete mode 100644 src/ior.rs delete mode 100644 src/protocol/domainsep/mod.rs create mode 100644 src/protocol/transcript/mod.rs create mode 100644 src/protocol/transcript/prover.rs create mode 100644 src/protocol/transcript/verifier.rs diff --git a/src/ior.rs b/src/ior.rs deleted file mode 100644 index 4654fe2..0000000 --- a/src/ior.rs +++ /dev/null @@ -1,61 +0,0 @@ -use ark_ff::Field; -use spongefish::{ProverState, VerifierState}; - -use crate::error::{DeciderError, ProverError, VerifierError}; - -/// An interactive oracle reduction transforms a claim on an input relation -/// into a claim on an (ideally simpler) output relation, -/// through prover/verifier interaction. -/// -/// This trait captures the general pattern described in the WARP paper: -/// InputRelation --IOR--> OutputRelation -/// -/// The prover produces a proof alongside the output claim. -/// The verifier, given the input instance and proof, derives the output instance. -pub trait InteractiveOracleReduction { - /// The relation being reduced FROM (input claim). - type InputInstance; - type InputWitness; - - /// The relation being reduced TO (output claim). - type OutputInstance; - type OutputWitness; - - /// Proof messages produced during this reduction step. - type Proof; - - /// Parameters needed for this reduction (e.g., index, code, Merkle params). - type Parameters; - - /// Prover side of the reduction: given an input claim (instance + witness), - /// interact with the transcript and produce an output claim + proof. - fn reduce_prove( - params: &Self::Parameters, - prover_state: &mut ProverState, - input_instance: &Self::InputInstance, - input_witness: &Self::InputWitness, - ) -> Result<(Self::OutputInstance, Self::OutputWitness, Self::Proof), ProverError>; - - /// Verifier side of the reduction: given an input instance + proof, - /// interact with the transcript and derive the output instance. - fn reduce_verify<'a>( - params: &Self::Parameters, - verifier_state: &mut VerifierState<'a>, - input_instance: &Self::InputInstance, - proof: &Self::Proof, - ) -> Result; -} - -/// A relation that can be checked directly without further reduction. -/// This is the "base case" — the decider checks the final accumulator. -pub trait Decidable { - type Instance; - type Witness; - type Parameters; - - fn decide( - params: &Self::Parameters, - instance: &Self::Instance, - witness: &Self::Witness, - ) -> Result<(), DeciderError>; -} diff --git a/src/lib.rs b/src/lib.rs index 8797c2b..ade683d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,9 @@ #![allow(clippy::assign_op_pattern)] // generated by SmallFpConfig derive macro use crate::error::WARPError; use crate::traits::AccumulationScheme; -use crate::types::{AccumulatorInstance, AccumulatorWitness, PesatOutput, WARPParams, WARPProof}; +use crate::types::{ + AccumulatorInstance, AccumulatorWitness, PesatOutput, ProveResult, WARPParams, WARPProof, +}; use ark_codes::traits::LinearCode; use ark_crypto_primitives::{ crh::{CRHScheme, TwoToOneCRHScheme}, @@ -13,6 +15,7 @@ use ark_poly::{ MultilinearExtension, Polynomial, }; use ark_std::log2; +use config::WARPConfig; use crypto::merkle::build_codeword_leaves; use crypto::merkle::compute_auth_paths; use efficient_sumcheck::{ @@ -23,8 +26,10 @@ use efficient_sumcheck::{ inner_product_sumcheck, order_strategy::AscendingOrder, }; -use protocol::domainsep::parse_statement; use protocol::query::QueryIndices; +use protocol::transcript::{ + absorb_instances, derive_randomness, parse_statement, DerivedRandomness, +}; use relations::{r1cs::R1CSConstraints, BundledPESAT}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; @@ -34,9 +39,6 @@ use utils::{ poly::{eq_poly, eq_poly_non_binary}, }; -use config::WARPConfig; -use protocol::domainsep::{absorb_accumulated_instances, absorb_instances, derive_randomness}; - pub mod config; pub mod constraints; pub mod crypto; @@ -186,7 +188,7 @@ impl< prover_state: &mut ProverState, witnesses: &[Vec], l1: usize, - #[allow(non_snake_case)] log_M: usize, + log_m: usize, ) -> Result, ProverError> { // a. encode witnesses let (codewords, leaves) = build_codeword_leaves(&self.params.code, witnesses, l1); @@ -212,7 +214,7 @@ impl< // e. zero check randomness and f. bundled evaluations let taus = (0..l1) - .map(|_| prover_state.verifier_messages_vec::(log_M)) + .map(|_| prover_state.verifier_messages_vec::(log_m)) .collect::>(); Ok(PesatOutput { @@ -245,17 +247,10 @@ impl< #[allow(non_snake_case)] N: usize, k: usize, log_l: usize, - ) -> Result< - ( - (AccumulatorInstance, AccumulatorWitness), - WARPProof, - ), - ProverError, - > { + ) -> ProveResult { let n = self.params.code.code_len(); let log_n = log2(n) as usize; - #[allow(non_snake_case)] - let log_M = log2(M) as usize; + let log_m = log2(M) as usize; let l1 = pesat.codewords.len(); // a. zero check randomness @@ -292,7 +287,7 @@ impl< let mut pw = [tau_eq_evals]; // tau let r1cs = self.params.p.constraints(); - let expected_num_coeffs = 2 + (log_n + 1).max(log_M + 2); + let expected_num_coeffs = 2 + (log_n + 1).max(log_m + 2); let sc = coefficient_sumcheck( |tw, pw| twin_constraint_round_poly(tw, pw, r1cs, omega, expected_num_coeffs), &mut tablewise, @@ -502,18 +497,17 @@ impl< //////////////////////// #[allow(non_snake_case)] let (M, N, k) = (pk.1, pk.2, pk.3); - #[allow(non_snake_case)] - let (log_M, log_l) = (log2(M) as usize, log2(l) as usize); + let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); debug_assert_eq!(instances[0].len(), N - k); absorb_instances(prover_state, &instances); - absorb_accumulated_instances::(prover_state, &acc_instance); + acc_instance.absorb_into(prover_state); //////////////////////// // 2. PESAT Reduction //////////////////////// - let pesat = self.pesat_reduce(prover_state, &witnesses, l1, log_M)?; + let pesat = self.pesat_reduce(prover_state, &witnesses, l1, log_m)?; //////////////////////// // 3. Constrained Code Accumulation @@ -548,8 +542,7 @@ impl< // a. verification key #[allow(non_snake_case)] let (M, N, k) = (vk.0, vk.1, vk.2); - #[allow(non_snake_case)] - let (log_M, log_l) = (log2(M) as usize, log2(l) as usize); + let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); let n = self.params.code.code_len(); let log_n = log2(n) as usize; @@ -564,12 +557,12 @@ impl< beta: (l2_taus, l2_xs), eta: l2_etas, }, - ) = parse_statement::(verifier_state, l1, l2, N - k, log_n, log_M)?; + ) = parse_statement::(verifier_state, l1, l2, N - k, log_n, log_m)?; //////////////////////// // 2. Derive randomness //////////////////////// - let ( + let DerivedRandomness { rt_0, l1_mus, l1_taus, @@ -577,7 +570,7 @@ impl< tau, gamma_sumcheck, coeffs_twinc_sumcheck, - _rt, + td: _, eta, mut nus, ood_samples, @@ -585,14 +578,14 @@ impl< xi, alpha_sumcheck, sums_batching_sumcheck, - ) = derive_randomness::( + } = derive_randomness::( verifier_state, l1, log_n, log_l, self.params.config.s, self.params.config.t, - log_M, + log_m, )?; let r = 1 + self.params.config.s + self.params.config.t; diff --git a/src/protocol/domainsep/mod.rs b/src/protocol/domainsep/mod.rs deleted file mode 100644 index a3dd928..0000000 --- a/src/protocol/domainsep/mod.rs +++ /dev/null @@ -1,247 +0,0 @@ -use ark_crypto_primitives::merkle_tree::Config; -use ark_ff::Field; -use ark_std::log2; - -use spongefish::{ - Decoding, Encoding, NargDeserialize, ProverState, VerificationResult, VerifierState, -}; - -use crate::types::AccumulatorInstance; - -pub fn absorb_instances>( - prover_state: &mut ProverState, - instances: &[Vec], -) { - for instance in instances { - for f in instance { - prover_state.prover_message(f); - } - } -} - -pub fn absorb_accumulated_instances< - F: Field + Encoding<[u8]>, - MT: Config + From<[u8; 32]>>, ->( - prover_state: &mut ProverState, - acc_instance: &AccumulatorInstance, -) { - // digests (rt) - for digest in &acc_instance.rt { - let bytes: [u8; 32] = digest.as_ref().try_into().expect("digest must be 32 bytes"); - prover_state.prover_message(&bytes); - } - - // alpha - for alpha in &acc_instance.alpha { - for f in alpha { - prover_state.prover_message(f); - } - } - - // mu - for f in &acc_instance.mu { - prover_state.prover_message(f); - } - - // taus - for tau in &acc_instance.beta.0 { - for f in tau { - prover_state.prover_message(f); - } - } - - // xs - for x in &acc_instance.beta.1 { - for f in x { - prover_state.prover_message(f); - } - } - - // etas - for f in &acc_instance.eta { - prover_state.prover_message(f); - } -} - -pub type ParsedStatement = (Vec>, AccumulatorInstance); - -pub fn parse_statement< - F: Field + NargDeserialize + Encoding<[u8]> + Decoding<[u8]>, - MT: Config + From<[u8; 32]>>, ->( - verifier_state: &mut VerifierState<'_>, - l1: usize, - l2: usize, - instance_len: usize, - log_n: usize, - #[allow(non_snake_case)] log_M: usize, -) -> VerificationResult> { - // f. absorb l1 instances - let mut l1_xs = Vec::with_capacity(l1); - for _ in 0..l1 { - let inst: Vec = verifier_state.prover_messages_vec(instance_len)?; - l1_xs.push(inst); - } - - // l2 instances - let mut l2_roots = Vec::with_capacity(l2); - for _ in 0..l2 { - let bytes: [u8; 32] = verifier_state.prover_message()?; - l2_roots.push(bytes.into()); - } - - let mut l2_alphas = Vec::with_capacity(l2); - for _ in 0..l2 { - let alpha: Vec = verifier_state.prover_messages_vec(log_n)?; - l2_alphas.push(alpha); - } - - let l2_mus: Vec = verifier_state.prover_messages_vec(l2)?; - - let mut l2_taus = Vec::with_capacity(l2); - for _ in 0..l2 { - let tau: Vec = verifier_state.prover_messages_vec(log_M)?; - l2_taus.push(tau); - } - - let mut l2_xs = Vec::with_capacity(l2); - for _ in 0..l2 { - let x: Vec = verifier_state.prover_messages_vec(instance_len)?; - l2_xs.push(x); - } - - let l2_etas: Vec = verifier_state.prover_messages_vec(l2)?; - - Ok(( - l1_xs, - AccumulatorInstance { - rt: l2_roots, - alpha: l2_alphas, - mu: l2_mus, - beta: (l2_taus, l2_xs), - eta: l2_etas, - }, - )) -} - -pub type DerivedRandomness = ( - ::InnerDigest, - Vec, - Vec>, - F, - Vec, - Vec, - Vec>, - ::InnerDigest, - F, - Vec, - Vec, - Vec, - Vec, - Vec, - Vec<[F; 3]>, -); - -pub fn derive_randomness< - F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, - MT: Config + From<[u8; 32]>>, ->( - verifier_state: &mut VerifierState<'_>, - l1: usize, - log_n: usize, - log_l: usize, - s: usize, - t: usize, - #[allow(non_snake_case)] log_M: usize, -) -> VerificationResult> { - // read commitment digest - let rt_0_bytes: [u8; 32] = verifier_state.prover_message()?; - let rt_0: MT::InnerDigest = rt_0_bytes.into(); - - // read mus - let l1_mus: Vec = verifier_state.prover_messages_vec(l1)?; - - // challenge taus - let mut l1_taus = Vec::with_capacity(l1); - for _ in 0..l1 { - let tau: Vec = (0..log_M) - .map(|_| verifier_state.verifier_message::()) - .collect(); - l1_taus.push(tau); - } - - let omega: F = verifier_state.verifier_message(); - let tau: Vec = (0..log_l) - .map(|_| verifier_state.verifier_message::()) - .collect(); - - // e. twin constraints sumcheck - let mut gamma_sumcheck = Vec::new(); - let mut coeffs_twinc_sumcheck = Vec::new(); - for _ in 0..log_l { - let h_coeffs: Vec = - verifier_state.prover_messages_vec(2 + (log_n + 1).max(log_M + 2))?; - let c: F = verifier_state.verifier_message(); - gamma_sumcheck.push(c); - coeffs_twinc_sumcheck.push(h_coeffs); - } - - // read td digest - let td_bytes: [u8; 32] = verifier_state.prover_message()?; - let _td: MT::InnerDigest = td_bytes.into(); - - // read eta and nu_0 - let eta: F = verifier_state.prover_message()?; - let nu_0: F = verifier_state.prover_message()?; - let mut nus = vec![nu_0]; - - // g. ood samples - let n_ood_samples = s * log_n; - let ood_samples: Vec = (0..n_ood_samples) - .map(|_| verifier_state.verifier_message::()) - .collect(); - - // h. ood answers - let ood_answers: Vec = verifier_state.prover_messages_vec(s)?; - nus.extend(ood_answers); - - // i. shift queries and zero check - let r = 1 + s + t; - let log_r = log2(r) as usize; - let n_shift_queries = (t * log_n).div_ceil(8); - let bytes_shift_queries: Vec = (0..n_shift_queries) - .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) - .collect(); - let xi: Vec = (0..log_r) - .map(|_| verifier_state.verifier_message::()) - .collect(); - - // j. batching sumcheck - let mut alpha_sumcheck = Vec::new(); - let mut sums_batching_sumcheck = Vec::new(); - for _ in 0..log_n { - let sums: [F; 3] = verifier_state.prover_messages()?; - let c: F = verifier_state.verifier_message(); - alpha_sumcheck.push(c); - sums_batching_sumcheck.push(sums); - } - - Ok(( - rt_0, - l1_mus, - l1_taus, - omega, - tau, - gamma_sumcheck, - coeffs_twinc_sumcheck, - _td, - eta, - nus, - ood_samples, - bytes_shift_queries, - xi, - alpha_sumcheck, - sums_batching_sumcheck, - )) -} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index d7fa0fa..fc4cd34 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,2 +1,2 @@ -pub mod domainsep; pub mod query; +pub mod transcript; diff --git a/src/protocol/query.rs b/src/protocol/query.rs index c727d93..1921af9 100644 --- a/src/protocol/query.rs +++ b/src/protocol/query.rs @@ -24,7 +24,8 @@ impl QueryIndices { // format the queries from squeezed bytes pub fn from_squeezed_bytes(squeezed_bytes: &[u8], log_n: usize, count: usize) -> Self { - let evaluation_points = Self::evaluation_points_from_squeezed_bytes(squeezed_bytes, log_n, count); + let evaluation_points = + Self::evaluation_points_from_squeezed_bytes(squeezed_bytes, log_n, count); // Compute all leaf positions in one batch let leaf_positions = Self::leaf_positions_from_evaluation_points(&evaluation_points); Self { @@ -63,9 +64,9 @@ impl QueryIndices { #[cfg(test)] mod tests { use super::*; + use crate::utils::fields::Goldilocks; use ark_bls12_381::Fr as BLS12_381; use ark_std::{One, Zero}; - use crate::utils::fields::Goldilocks; // check dimensions, binary values, and leaf position range fn check_query_indices(bytes: &[u8], log_n: usize, num_queries: usize) { diff --git a/src/protocol/transcript/mod.rs b/src/protocol/transcript/mod.rs new file mode 100644 index 0000000..2283192 --- /dev/null +++ b/src/protocol/transcript/mod.rs @@ -0,0 +1,5 @@ +pub mod prover; +pub mod verifier; + +pub use prover::*; +pub use verifier::*; diff --git a/src/protocol/transcript/prover.rs b/src/protocol/transcript/prover.rs new file mode 100644 index 0000000..c755b0b --- /dev/null +++ b/src/protocol/transcript/prover.rs @@ -0,0 +1,57 @@ +use ark_ff::Field; +use spongefish::{Encoding, ProverState}; + +use crate::types::AccumulatorInstance; +use ark_crypto_primitives::merkle_tree::Config; + +// absorb a list of plain instances into the transcript +pub fn absorb_instances>( + prover_state: &mut ProverState, + instances: &[Vec], +) { + for instance in instances { + for f in instance { + prover_state.prover_message(f); + } + } +} + +// absorb an AccumulatorInstance into the transcript +impl< + F: Field + Encoding<[u8]>, + MT: Config + From<[u8; 32]>>, + > AccumulatorInstance +{ + pub fn absorb_into(&self, prover_state: &mut ProverState) { + for digest in &self.rt { + let bytes: [u8; 32] = digest.as_ref().try_into().expect("digest must be 32 bytes"); + prover_state.prover_message(&bytes); + } + + for alpha in &self.alpha { + for f in alpha { + prover_state.prover_message(f); + } + } + + for f in &self.mu { + prover_state.prover_message(f); + } + + for tau in &self.beta.0 { + for f in tau { + prover_state.prover_message(f); + } + } + + for x in &self.beta.1 { + for f in x { + prover_state.prover_message(f); + } + } + + for f in &self.eta { + prover_state.prover_message(f); + } + } +} diff --git a/src/protocol/transcript/verifier.rs b/src/protocol/transcript/verifier.rs new file mode 100644 index 0000000..21865a9 --- /dev/null +++ b/src/protocol/transcript/verifier.rs @@ -0,0 +1,199 @@ +use ark_crypto_primitives::merkle_tree::Config; +use ark_ff::Field; +use ark_std::log2; + +use spongefish::{Decoding, Encoding, NargDeserialize, VerificationResult, VerifierState}; + +use crate::types::AccumulatorInstance; + +// (l1 instances, accumulated instance) +pub type ParsedStatement = (Vec>, AccumulatorInstance); + +// parse l1 plain instances + an AccumulatorInstance from the transcript +pub fn parse_statement< + F: Field + NargDeserialize + Encoding<[u8]> + Decoding<[u8]>, + MT: Config + From<[u8; 32]>>, +>( + verifier_state: &mut VerifierState<'_>, + l1: usize, + l2: usize, + instance_len: usize, + log_n: usize, + log_m: usize, +) -> VerificationResult> { + let l1_xs: Vec> = (0..l1) + .map(|_| verifier_state.prover_messages_vec(instance_len)) + .collect::>()?; + + let acc = + AccumulatorInstance::::parse_from(verifier_state, l2, log_n, log_m, instance_len)?; + + Ok((l1_xs, acc)) +} + +// parse an AccumulatorInstance from the verifier transcript +impl< + F: Field + NargDeserialize + Encoding<[u8]> + Decoding<[u8]>, + MT: Config + From<[u8; 32]>>, + > AccumulatorInstance +{ + pub fn parse_from( + verifier_state: &mut VerifierState<'_>, + l2: usize, + log_n: usize, + log_m: usize, + instance_len: usize, + ) -> VerificationResult { + let rt: Vec = (0..l2) + .map(|_| -> VerificationResult<_> { + let bytes: [u8; 32] = verifier_state.prover_message()?; + Ok(bytes.into()) + }) + .collect::>()?; + + let alpha: Vec> = (0..l2) + .map(|_| verifier_state.prover_messages_vec(log_n)) + .collect::>()?; + + let mu: Vec = verifier_state.prover_messages_vec(l2)?; + + let taus: Vec> = (0..l2) + .map(|_| verifier_state.prover_messages_vec(log_m)) + .collect::>()?; + + let xs: Vec> = (0..l2) + .map(|_| verifier_state.prover_messages_vec(instance_len)) + .collect::>()?; + + let eta: Vec = verifier_state.prover_messages_vec(l2)?; + + Ok(Self { + rt, + alpha, + mu, + beta: (taus, xs), + eta, + }) + } +} + +pub struct DerivedRandomness { + pub rt_0: MT::InnerDigest, + pub l1_mus: Vec, + pub l1_taus: Vec>, + pub omega: F, + pub tau: Vec, + pub gamma_sumcheck: Vec, + pub coeffs_twinc_sumcheck: Vec>, + pub td: MT::InnerDigest, + pub eta: F, + pub nus: Vec, + pub ood_samples: Vec, + pub bytes_shift_queries: Vec, + pub xi: Vec, + pub alpha_sumcheck: Vec, + pub sums_batching_sumcheck: Vec<[F; 3]>, +} + +pub fn derive_randomness< + F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, + MT: Config + From<[u8; 32]>>, +>( + verifier_state: &mut VerifierState<'_>, + l1: usize, + log_n: usize, + log_l: usize, + s: usize, + t: usize, + log_m: usize, +) -> VerificationResult> { + // commitment digest + let rt_0_bytes: [u8; 32] = verifier_state.prover_message()?; + let rt_0: MT::InnerDigest = rt_0_bytes.into(); + + // mus + let l1_mus: Vec = verifier_state.prover_messages_vec(l1)?; + + // challenge taus + let l1_taus: Vec> = (0..l1) + .map(|_| { + (0..log_m) + .map(|_| verifier_state.verifier_message::()) + .collect() + }) + .collect(); + + let omega: F = verifier_state.verifier_message(); + let tau: Vec = (0..log_l) + .map(|_| verifier_state.verifier_message::()) + .collect(); + + // twin constraints sumcheck + let mut gamma_sumcheck = Vec::new(); + let mut coeffs_twinc_sumcheck = Vec::new(); + for _ in 0..log_l { + let h_coeffs: Vec = + verifier_state.prover_messages_vec(2 + (log_n + 1).max(log_m + 2))?; + let c: F = verifier_state.verifier_message(); + gamma_sumcheck.push(c); + coeffs_twinc_sumcheck.push(h_coeffs); + } + + // td digest + let td_bytes: [u8; 32] = verifier_state.prover_message()?; + let td: MT::InnerDigest = td_bytes.into(); + + // eta and nu_0 + let eta: F = verifier_state.prover_message()?; + let nu_0: F = verifier_state.prover_message()?; + let mut nus = vec![nu_0]; + + // ood samples + let n_ood_samples = s * log_n; + let ood_samples: Vec = (0..n_ood_samples) + .map(|_| verifier_state.verifier_message::()) + .collect(); + + // ood answers + let ood_answers: Vec = verifier_state.prover_messages_vec(s)?; + nus.extend(ood_answers); + + // shift queries and zero check + let r = 1 + s + t; + let log_r = log2(r) as usize; + let n_shift_queries = (t * log_n).div_ceil(8); + let bytes_shift_queries: Vec = (0..n_shift_queries) + .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) + .collect(); + let xi: Vec = (0..log_r) + .map(|_| verifier_state.verifier_message::()) + .collect(); + + // batching sumcheck + let mut alpha_sumcheck = Vec::new(); + let mut sums_batching_sumcheck = Vec::new(); + for _ in 0..log_n { + let sums: [F; 3] = verifier_state.prover_messages()?; + let c: F = verifier_state.verifier_message(); + alpha_sumcheck.push(c); + sums_batching_sumcheck.push(sums); + } + + Ok(DerivedRandomness { + rt_0, + l1_mus, + l1_taus, + omega, + tau, + gamma_sumcheck, + coeffs_twinc_sumcheck, + td, + eta, + nus, + ood_samples, + bytes_shift_queries, + xi, + alpha_sumcheck, + sums_batching_sumcheck, + }) +} diff --git a/src/traits.rs b/src/traits.rs index e5b3bd9..7f0b8f2 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -2,8 +2,8 @@ use ark_crypto_primitives::merkle_tree::Config; use ark_ff::Field; use spongefish::{ProverState, VerificationResult, VerifierState}; -use crate::error::{ProverError, VerifierError, WARPError}; -use crate::types::{AccumulatorInstance, AccumulatorWitness, WARPProof}; +use crate::error::{VerifierError, WARPError}; +use crate::types::{AccumulatorInstance, AccumulatorWitness, ProveResult, WARPProof}; pub trait AccumulationScheme { type Index; @@ -27,13 +27,7 @@ pub trait AccumulationScheme { instances: Self::Instances, acc_instance: AccumulatorInstance, acc_witness: AccumulatorWitness, - ) -> Result< - ( - (AccumulatorInstance, AccumulatorWitness), - WARPProof, - ), - ProverError, - >; + ) -> ProveResult; fn verify<'a>( &self, diff --git a/src/types.rs b/src/types.rs index 6434bb6..dbb8de7 100644 --- a/src/types.rs +++ b/src/types.rs @@ -7,8 +7,18 @@ use ark_ff::Field; use std::marker::PhantomData; use crate::config::WARPConfig; +use crate::error::ProverError; use crate::relations::BundledPESAT; +// result of a prove call: (new accumulator instance + witness, proof) +pub type ProveResult = Result< + ( + (AccumulatorInstance, AccumulatorWitness), + WARPProof, + ), + ProverError, +>; + /// Protocol parameters for WARP — the shared configuration used by all IOR phases. pub struct WARPParams, C: LinearCode + Clone, MT: Config> { pub _f: PhantomData, From e4920ef3366d8675375da336bc437fcae0c8bd60 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Wed, 25 Mar 2026 22:08:04 +0100 Subject: [PATCH 04/68] fix upstream crypto primitives --- Cargo.toml | 2 +- src/crypto/blake3_crh/fields.rs | 35 --------------- src/crypto/blake3_crh/mod.rs | 70 ----------------------------- src/crypto/merkle/blake3.rs | 26 ----------- src/crypto/merkle/mod.rs | 4 -- src/crypto/merkle/parameters.rs | 76 -------------------------------- src/crypto/merkle/poseidon.rs | 52 ---------------------- src/crypto/mod.rs | 1 - src/lib.rs | 78 ++++++++++++++++----------------- 9 files changed, 38 insertions(+), 306 deletions(-) delete mode 100644 src/crypto/blake3_crh/fields.rs delete mode 100644 src/crypto/blake3_crh/mod.rs delete mode 100644 src/crypto/merkle/blake3.rs delete mode 100644 src/crypto/merkle/parameters.rs delete mode 100644 src/crypto/merkle/poseidon.rs diff --git a/Cargo.toml b/Cargo.toml index 3a3440a..d8a6ecd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ ark-codes = { git = "https://github.com/dmpierre/ark-codes.git" } [patch.crates-io] -ark-crypto-primitives = { git = "https://github.com/benbencik/crypto-primitives.git", branch = "smallfp-absorb-clean" } +ark-crypto-primitives = { git = "https://github.com/arkworks-rs/crypto-primitives.git", branch = "z-tech/smallfp-absorb" } ark-ff = { git = "https://github.com/arkworks-rs/algebra.git" } ark-poly = { git = "https://github.com/arkworks-rs/algebra.git" } ark-serialize = { git = "https://github.com/arkworks-rs/algebra.git" } diff --git a/src/crypto/blake3_crh/fields.rs b/src/crypto/blake3_crh/fields.rs deleted file mode 100644 index 0cc9417..0000000 --- a/src/crypto/blake3_crh/fields.rs +++ /dev/null @@ -1,35 +0,0 @@ -use ark_ff::Field; -use ark_serialize::CanonicalSerialize; -use ark_std::rand::RngCore; -use core::borrow::Borrow; -use core::marker::PhantomData; - -use ark_crypto_primitives::{crh::CRHScheme, Error}; - -use super::GenericDigest; - -/// Blake3 leaf hash that takes field elements as input. -#[derive(Clone)] -pub struct Blake3F { - _f: PhantomData, -} - -impl CRHScheme for Blake3F { - type Input = [F]; - type Output = GenericDigest<32>; - type Parameters = (); - - fn setup(_: &mut R) -> Result { - Ok(()) - } - - fn evaluate>( - (): &Self::Parameters, - input: T, - ) -> Result { - let mut buf = Vec::new(); - input.borrow().serialize_compressed(&mut buf)?; - let output: [_; 32] = blake3::hash(&buf).into(); - Ok(output.into()) - } -} diff --git a/src/crypto/blake3_crh/mod.rs b/src/crypto/blake3_crh/mod.rs deleted file mode 100644 index 404033b..0000000 --- a/src/crypto/blake3_crh/mod.rs +++ /dev/null @@ -1,70 +0,0 @@ -pub mod fields; - -use ark_crypto_primitives::{crh::TwoToOneCRHScheme, sponge::Absorb, Error}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use ark_std::rand::RngCore; -use core::borrow::Borrow; - -/// A generic fixed-size digest (copied from whir). -#[derive(Clone, Debug, Eq, PartialEq, Hash, CanonicalSerialize, CanonicalDeserialize)] -pub struct GenericDigest(pub [u8; N]); - -impl Default for GenericDigest { - fn default() -> Self { - Self([0; N]) - } -} - -impl AsRef<[u8]> for GenericDigest { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl From<[u8; N]> for GenericDigest { - fn from(value: [u8; N]) -> Self { - Self(value) - } -} - -impl Absorb for GenericDigest { - fn to_sponge_bytes(&self, dest: &mut Vec) { - dest.extend_from_slice(&self.0); - } - - fn to_sponge_field_elements(&self, dest: &mut Vec) { - dest.push(F::from_be_bytes_mod_order(&self.0)); - } -} - -/// Blake3 two-to-one hash for internal Merkle tree nodes. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Blake3; - -impl TwoToOneCRHScheme for Blake3 { - type Input = GenericDigest<32>; - type Output = GenericDigest<32>; - type Parameters = (); - - fn setup(_: &mut R) -> Result { - Ok(()) - } - - fn evaluate>( - (): &Self::Parameters, - left_input: T, - right_input: T, - ) -> Result { - let output: [_; 32] = - blake3::hash(&[left_input.borrow().0, right_input.borrow().0].concat()).into(); - Ok(output.into()) - } - - fn compress>( - parameters: &Self::Parameters, - left_input: T, - right_input: T, - ) -> Result { - Self::evaluate(parameters, left_input, right_input) - } -} diff --git a/src/crypto/merkle/blake3.rs b/src/crypto/merkle/blake3.rs deleted file mode 100644 index e6ca88c..0000000 --- a/src/crypto/merkle/blake3.rs +++ /dev/null @@ -1,26 +0,0 @@ -use super::parameters::MerkleTreeParams; -use crate::crypto::blake3_crh::fields::Blake3F; -use crate::crypto::blake3_crh::{Blake3, GenericDigest}; -use ark_crypto_primitives::{ - crh::{CRHScheme, TwoToOneCRHScheme}, - merkle_tree::{Config as MerkleConfig, IdentityDigestConverter}, - sponge::Absorb, -}; -use ark_ff::PrimeField; -use ark_std::marker::PhantomData; - -#[derive(Clone)] -pub struct Blake3MerkleConfig { - _field: PhantomData, -} - -pub type Blake3MerkleTreeParams = MerkleTreeParams, Blake3, GenericDigest<32>>; - -impl MerkleConfig for Blake3MerkleConfig { - type Leaf = [F]; - type LeafDigest = ::Output; - type LeafInnerDigestConverter = IdentityDigestConverter; - type InnerDigest = ::Output; - type LeafHash = Blake3F; // blake3, over field elements - type TwoToOneHash = Blake3; -} diff --git a/src/crypto/merkle/mod.rs b/src/crypto/merkle/mod.rs index 43b2474..9330bf7 100644 --- a/src/crypto/merkle/mod.rs +++ b/src/crypto/merkle/mod.rs @@ -5,10 +5,6 @@ use ark_crypto_primitives::{ }; use ark_ff::Field; -pub mod blake3; -pub mod parameters; -pub mod poseidon; - pub fn build_codeword_leaves>( code: &C, witnesses: &[Vec], diff --git a/src/crypto/merkle/parameters.rs b/src/crypto/merkle/parameters.rs deleted file mode 100644 index 19667d7..0000000 --- a/src/crypto/merkle/parameters.rs +++ /dev/null @@ -1,76 +0,0 @@ -use ark_crypto_primitives::{ - crh::{CRHScheme, TwoToOneCRHScheme}, - merkle_tree::{Config, IdentityDigestConverter}, - sponge::Absorb, -}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use ark_std::rand::RngCore; -use serde::Deserialize; -use serde::Serialize; -use std::{hash::Hash, marker::PhantomData}; - -/// A generic Merkle tree config usable across hash types (e.g., Blake3, Keccak). -/// -/// # Type Parameters: -/// - `F`: Field element used in the leaves -/// - `LeafH`: Leaf hash function -/// - `CompressH`: Internal node hasher -/// - `Digest`: Digest type -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -#[serde(bound = "")] -pub struct MerkleTreeParams { - #[serde(skip)] - _marker: PhantomData<(F, LeafH, CompressH, Digest)>, -} - -impl Config for MerkleTreeParams -where - F: CanonicalSerialize + Send, - LeafH: CRHScheme, - CompressH: TwoToOneCRHScheme, - Digest: Clone - + std::fmt::Debug - + Default - + CanonicalSerialize - + CanonicalDeserialize - + Eq - + PartialEq - + Hash - + Send - + Absorb, -{ - type Leaf = [F]; - - type LeafDigest = Digest; - type LeafInnerDigestConverter = IdentityDigestConverter; - type InnerDigest = Digest; - - type LeafHash = LeafH; - type TwoToOneHash = CompressH; -} - -/// Returns the `(leaf_hash_params, two_to_one_hash_params)` for any compatible Merkle tree. -/// -/// # Type Parameters -/// - `F`: The leaf field element type -/// - `LeafH`: The leaf hash function -/// - `CompressH`: The two-to-one internal hash function -/// -/// # Panics -/// Panics if `setup()` fails (which should not happen for deterministic hashers). -pub fn default_config( - rng: &mut impl RngCore, -) -> ( - ::Parameters, - ::Parameters, -) -where - F: CanonicalSerialize + Send, - LeafH: CRHScheme + Send, - CompressH: TwoToOneCRHScheme + Send, -{ - ( - LeafH::setup(rng).expect("Failed to setup Leaf hash"), - CompressH::setup(rng).expect("Failed to setup Compress hash"), - ) -} diff --git a/src/crypto/merkle/poseidon.rs b/src/crypto/merkle/poseidon.rs deleted file mode 100644 index f5a989d..0000000 --- a/src/crypto/merkle/poseidon.rs +++ /dev/null @@ -1,52 +0,0 @@ -use ark_crypto_primitives::{ - crh::{ - poseidon::{ - constraints::{ - CRHGadget as PoseidonCRHGadget, TwoToOneCRHGadget as PoseidonTwoToOneCRHGadget, - }, - TwoToOneCRH as PoseidonTwoToOneCRH, CRH as PoseidonCRH, - }, - CRHScheme, CRHSchemeGadget, TwoToOneCRHScheme, TwoToOneCRHSchemeGadget, - }, - merkle_tree::{ - constraints::ConfigGadget as MerkleConfigGadget, Config as MerkleConfig, - IdentityDigestConverter, - }, - sponge::Absorb, -}; -use ark_ff::PrimeField; -use ark_r1cs_std::fields::fp::FpVar; -use ark_std::marker::PhantomData; - -#[derive(Clone)] -pub struct PoseidonMerkleConfig { - _field: PhantomData, -} - -impl MerkleConfig for PoseidonMerkleConfig { - type Leaf = [F]; - type LeafDigest = ::Output; - type LeafInnerDigestConverter = IdentityDigestConverter; - type InnerDigest = ::Output; - type LeafHash = PoseidonCRH; - type TwoToOneHash = PoseidonTwoToOneCRH; -} - -#[derive(Clone)] -pub struct PoseidonMerkleConfigGadget { - _field: PhantomData, -} - -impl MerkleConfigGadget, F> - for PoseidonMerkleConfigGadget -{ - type Leaf = [FpVar]; - type LeafDigest = as CRHSchemeGadget, F>>::OutputVar; - type LeafHash = PoseidonCRHGadget; - type LeafInnerConverter = IdentityDigestConverter; - type InnerDigest = as TwoToOneCRHSchemeGadget< - PoseidonTwoToOneCRH, - F, - >>::OutputVar; - type TwoToOneHash = PoseidonTwoToOneCRHGadget; -} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 6a3489a..bdf9eb8 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1,2 +1 @@ -pub mod blake3_crh; pub mod merkle; diff --git a/src/lib.rs b/src/lib.rs index ade683d..911e540 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -795,7 +795,6 @@ impl< #[cfg(test)] pub mod test { - use super::crypto::merkle::blake3::Blake3MerkleTreeParams; use super::AccumulationScheme; use crate::serialize::{AccInstanceSerializer, AccWitnessSerializer, ProofSerializer}; use crate::types::{AccumulatorInstance, AccumulatorWitness}; @@ -811,6 +810,7 @@ pub mod test { }, utils::poseidon, }; + use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; use ark_bls12_381::Fr as BLS12_381; use ark_codes::{ @@ -873,14 +873,14 @@ pub mod test { .unwrap(); let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = WARP::< - BLS12_381, - R1CS, - _, - Blake3MerkleTreeParams, - >::new( - warp_config.clone(), code.clone(), r1cs.clone(), (), () - ); + let hash_chain_warp = + WARP::, _, Blake3MerkleConfig>::new( + warp_config.clone(), + code.clone(), + r1cs.clone(), + (), + (), + ); let (mut acc_roots, mut acc_alphas, mut acc_mus, mut acc_taus, mut acc_xs, mut acc_eta) = (vec![], vec![], vec![], vec![], vec![], vec![]); @@ -915,14 +915,14 @@ pub mod test { let warp_config = WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = WARP::< - BLS12_381, - R1CS, - _, - Blake3MerkleTreeParams, - >::new( - warp_config.clone(), code.clone(), r1cs.clone(), (), () - ); + let hash_chain_warp = + WARP::, _, Blake3MerkleConfig>::new( + warp_config.clone(), + code.clone(), + r1cs.clone(), + (), + (), + ); let mut prover_state = domainsep.instance(&0u32).std_prover(); let ((acc_x, acc_w), pf) = hash_chain_warp @@ -961,10 +961,8 @@ pub mod test { .decide(acc_w.clone(), acc_x.clone()) .unwrap(); - let acc_x_to_serde = - AccInstanceSerializer::<_, Blake3MerkleTreeParams>::new(acc_x); - let acc_w_to_serde = - AccWitnessSerializer::<_, Blake3MerkleTreeParams>::new(acc_w); + let acc_x_to_serde = AccInstanceSerializer::<_, Blake3MerkleConfig>::new(acc_x); + let acc_w_to_serde = AccWitnessSerializer::<_, Blake3MerkleConfig>::new(acc_w); let proof_to_serde = ProofSerializer::new(pf); println!( @@ -1031,14 +1029,14 @@ pub mod test { .unwrap(); let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = WARP::< - Goldilocks, - R1CS, - _, - Blake3MerkleTreeParams, - >::new( - warp_config.clone(), code.clone(), r1cs.clone(), (), () - ); + let hash_chain_warp = + WARP::, _, Blake3MerkleConfig>::new( + warp_config.clone(), + code.clone(), + r1cs.clone(), + (), + (), + ); let (mut acc_roots, mut acc_alphas, mut acc_mus, mut acc_taus, mut acc_xs, mut acc_eta) = (vec![], vec![], vec![], vec![], vec![], vec![]); @@ -1074,14 +1072,14 @@ pub mod test { let warp_config = WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = WARP::< - Goldilocks, - R1CS, - _, - Blake3MerkleTreeParams, - >::new( - warp_config.clone(), code.clone(), r1cs.clone(), (), () - ); + let hash_chain_warp = + WARP::, _, Blake3MerkleConfig>::new( + warp_config.clone(), + code.clone(), + r1cs.clone(), + (), + (), + ); let mut prover_state = domainsep.instance(&0u32).std_prover(); let ((acc_x, acc_w), pf) = hash_chain_warp @@ -1120,10 +1118,8 @@ pub mod test { .decide(acc_w.clone(), acc_x.clone()) .unwrap(); - let acc_x_to_serde = - AccInstanceSerializer::<_, Blake3MerkleTreeParams>::new(acc_x); - let acc_w_to_serde = - AccWitnessSerializer::<_, Blake3MerkleTreeParams>::new(acc_w); + let acc_x_to_serde = AccInstanceSerializer::<_, Blake3MerkleConfig>::new(acc_x); + let acc_w_to_serde = AccWitnessSerializer::<_, Blake3MerkleConfig>::new(acc_w); let proof_to_serde = ProofSerializer::new(pf); println!( From a66f1225a925dd6b7d1905f70b8e97653ad139c5 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Wed, 8 Apr 2026 15:10:09 +0200 Subject: [PATCH 05/68] comm complexity reduction inner product sumcheck api --- Cargo.toml | 8 ++-- benches/warp_rs.rs | 62 ++--------------------------- src/lib.rs | 11 ++--- src/protocol/transcript/verifier.rs | 4 +- 4 files changed, 14 insertions(+), 71 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d8a6ecd..1e20d5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,16 +23,16 @@ serde_json = "1.0" spongefish = { git = "https://github.com/z-tech/spongefish.git", branch = "smallfp-support", features = [ "ark-ff", ] } -efficient-sumcheck = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git" } +efficient-sumcheck = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git", branch = "z-tech/simd_goldilocks_experimental" } thiserror = "2.0.16" ark-codes = { git = "https://github.com/dmpierre/ark-codes.git" } [patch.crates-io] ark-crypto-primitives = { git = "https://github.com/arkworks-rs/crypto-primitives.git", branch = "z-tech/smallfp-absorb" } -ark-ff = { git = "https://github.com/arkworks-rs/algebra.git" } -ark-poly = { git = "https://github.com/arkworks-rs/algebra.git" } -ark-serialize = { git = "https://github.com/arkworks-rs/algebra.git" } +ark-ff = { git = "https://github.com/arkworks-rs/algebra.git", rev = "285dac2" } +ark-poly = { git = "https://github.com/arkworks-rs/algebra.git", rev = "285dac2" } +ark-serialize = { git = "https://github.com/arkworks-rs/algebra.git", rev = "285dac2" } ark-r1cs-std = { git = "https://github.com/arkworks-rs/r1cs-std" } ark-relations = { git = "https://github.com/arkworks-rs/snark.git" } ark-snark = { git = "https://github.com/arkworks-rs/snark.git" } diff --git a/benches/warp_rs.rs b/benches/warp_rs.rs index 1943dfd..f798892 100644 --- a/benches/warp_rs.rs +++ b/benches/warp_rs.rs @@ -1,4 +1,3 @@ -use ark_bls12_381::Fr as BLS12_381; use ark_codes::reed_solomon::config::ReedSolomonConfig; use ark_codes::reed_solomon::ReedSolomon; use ark_codes::traits::LinearCode; @@ -8,7 +7,7 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use utils::domainsep::init_prover_state; use utils::hash_chain::{get_hashchain_instance_witness_pairs, get_hashchain_r1cs}; use warp::config::WARPConfig; -use warp::crypto::merkle::blake3::Blake3MerkleTreeParams; +use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; use warp::traits::AccumulationScheme; use warp::WARP; @@ -19,59 +18,6 @@ use warp::utils::fields::Goldilocks; const HASHCHAIN_SIZE: usize = 800; -pub fn bench_rs_warp(c: &mut Criterion) { - let mut rng = thread_rng(); - let poseidon_config = poseidon::initialize_poseidon_config::(); - let r1cs = get_hashchain_r1cs(&poseidon_config, HASHCHAIN_SIZE); - - let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); - let code = ReedSolomon::new(code_config.clone()); - let s = 8; - let t = 7; - - for l in [32, 64, 128, 256, 512] { - let warp_config = WARPConfig::new(l, l, s, t, r1cs.config(), code.code_len()); - - let hash_chain_warp = WARP::<_, _, _, Blake3MerkleTreeParams<_>>::new( - warp_config.clone(), - code.clone(), - r1cs.clone(), - (), - (), - ); - - let instances_witnesses = - get_hashchain_instance_witness_pairs(l, &poseidon_config, HASHCHAIN_SIZE, &mut rng); - - let mut group = c.benchmark_group("warp_rs_bls12_381_hash_chain"); - group.sample_size(10); - group.bench_with_input( - BenchmarkId::from_parameter(l), - &instances_witnesses, - |b, instance_witnesses| { - b.iter_with_setup( - || { - let prover_state = init_prover_state(); - (prover_state, instance_witnesses.clone()) - }, - |(mut prover_state, _x_w)| { - let _ = hash_chain_warp - .prove( - (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), - &mut prover_state, - instances_witnesses.1.clone(), - instances_witnesses.0.clone(), - (vec![], vec![], vec![], (vec![], vec![]), vec![]), - (vec![], vec![], vec![]), - ) - .unwrap(); - }, - ); - }, - ); - } -} - pub fn bench_rs_warp_fields(c: &mut Criterion) { pub type F = Goldilocks; let mut rng = thread_rng(); @@ -86,7 +32,7 @@ pub fn bench_rs_warp_fields(c: &mut Criterion) { for l in [32, 64, 128, 256, 512] { let warp_config = WARPConfig::new(l, l, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = WARP::<_, _, _, Blake3MerkleTreeParams<_>>::new( + let hash_chain_warp = WARP::<_, _, _, Blake3MerkleConfig<_>>::new( warp_config.clone(), code.clone(), r1cs.clone(), @@ -115,8 +61,8 @@ pub fn bench_rs_warp_fields(c: &mut Criterion) { &mut prover_state, instances_witnesses.1.clone(), instances_witnesses.0.clone(), - (vec![], vec![], vec![], (vec![], vec![]), vec![]), - (vec![], vec![], vec![]), + warp::types::AccumulatorInstance::empty(), + warp::types::AccumulatorWitness::empty(), ) .unwrap(); }, diff --git a/src/lib.rs b/src/lib.rs index 911e540..8daf117 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -708,13 +708,10 @@ impl< // multilinear batching sumcheck (sums_batching_sumcheck.len() == log_n).ok_or_err(VerifierError::NumSumcheckRounds)?; let mut target_2 = sigma_2; - for ([sum_00, sum_11, sum_0110], alpha) in - sums_batching_sumcheck.into_iter().zip(&alpha_sumcheck) - { - (sum_00 + sum_11 == target_2).ok_or_err(VerifierError::SumcheckRound)?; - target_2 = (target_2 - sum_0110) * alpha.square() - + sum_00 * (F::one() - alpha.double()) - + sum_0110 * alpha; + for ([a, b], alpha) in sums_batching_sumcheck.into_iter().zip(&alpha_sumcheck) { + target_2 = (target_2 - b) * alpha.square() + + a * (F::one() - alpha.double()) + + b * alpha; } // e. new target decision diff --git a/src/protocol/transcript/verifier.rs b/src/protocol/transcript/verifier.rs index 21865a9..19ae9c6 100644 --- a/src/protocol/transcript/verifier.rs +++ b/src/protocol/transcript/verifier.rs @@ -92,7 +92,7 @@ pub struct DerivedRandomness { pub bytes_shift_queries: Vec, pub xi: Vec, pub alpha_sumcheck: Vec, - pub sums_batching_sumcheck: Vec<[F; 3]>, + pub sums_batching_sumcheck: Vec<[F; 2]>, } pub fn derive_randomness< @@ -173,7 +173,7 @@ pub fn derive_randomness< let mut alpha_sumcheck = Vec::new(); let mut sums_batching_sumcheck = Vec::new(); for _ in 0..log_n { - let sums: [F; 3] = verifier_state.prover_messages()?; + let sums: [F; 2] = verifier_state.prover_messages()?; let c: F = verifier_state.verifier_message(); alpha_sumcheck.push(c); sums_batching_sumcheck.push(sums); From 77067669aa25c218dcd2a50ff2bb45b5f10b39fb Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:27:10 +0200 Subject: [PATCH 06/68] chkpt --- Cargo.toml | 2 +- benches/warp_rs.rs | 2 +- src/lib.rs | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1e20d5b..dcef7af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ serde_json = "1.0" spongefish = { git = "https://github.com/z-tech/spongefish.git", branch = "smallfp-support", features = [ "ark-ff", ] } -efficient-sumcheck = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git", branch = "z-tech/simd_goldilocks_experimental" } +efficient-sumcheck = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git", rev = "913b26d" } thiserror = "2.0.16" ark-codes = { git = "https://github.com/dmpierre/ark-codes.git" } diff --git a/benches/warp_rs.rs b/benches/warp_rs.rs index f798892..2f238b8 100644 --- a/benches/warp_rs.rs +++ b/benches/warp_rs.rs @@ -2,12 +2,12 @@ use ark_codes::reed_solomon::config::ReedSolomonConfig; use ark_codes::reed_solomon::ReedSolomon; use ark_codes::traits::LinearCode; +use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; use ark_std::rand::thread_rng; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use utils::domainsep::init_prover_state; use utils::hash_chain::{get_hashchain_instance_witness_pairs, get_hashchain_r1cs}; use warp::config::WARPConfig; -use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; use warp::traits::AccumulationScheme; use warp::WARP; diff --git a/src/lib.rs b/src/lib.rs index 8daf117..3c3d8fa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -709,9 +709,8 @@ impl< (sums_batching_sumcheck.len() == log_n).ok_or_err(VerifierError::NumSumcheckRounds)?; let mut target_2 = sigma_2; for ([a, b], alpha) in sums_batching_sumcheck.into_iter().zip(&alpha_sumcheck) { - target_2 = (target_2 - b) * alpha.square() - + a * (F::one() - alpha.double()) - + b * alpha; + target_2 = + (target_2 - b) * alpha.square() + a * (F::one() - alpha.double()) + b * alpha; } // e. new target decision From dabc15011b51d81267760c89b4dd467109595379 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Wed, 8 Apr 2026 17:41:53 +0200 Subject: [PATCH 07/68] rm rev pinning sumcheck --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index dcef7af..1e20d5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ serde_json = "1.0" spongefish = { git = "https://github.com/z-tech/spongefish.git", branch = "smallfp-support", features = [ "ark-ff", ] } -efficient-sumcheck = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git", rev = "913b26d" } +efficient-sumcheck = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git", branch = "z-tech/simd_goldilocks_experimental" } thiserror = "2.0.16" ark-codes = { git = "https://github.com/dmpierre/ark-codes.git" } From 312e22049e753135d144776aece3cb68d32ebaad Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 9 Apr 2026 12:37:44 +0200 Subject: [PATCH 08/68] more opts --- src/lib.rs | 166 +++++++++++++++++++--------- src/protocol/transcript/verifier.rs | 2 +- 2 files changed, 112 insertions(+), 56 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3c3d8fa..fa4ae5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,7 @@ use ark_crypto_primitives::{ crh::{CRHScheme, TwoToOneCRHScheme}, merkle_tree::{Config, MerkleTree, Path}, }; -use ark_ff::{Field, PrimeField, Zero}; +use ark_ff::{Field, PrimeField}; use ark_poly::{ univariate::DensePolynomial, DenseMultilinearExtension, DenseUVPolynomial, MultilinearExtension, Polynomial, @@ -20,7 +20,7 @@ use crypto::merkle::build_codeword_leaves; use crypto::merkle::compute_auth_paths; use efficient_sumcheck::{ accumulate_sparse_evaluations, batched_constraint_poly, - coefficient_sumcheck::coefficient_sumcheck, + coefficient_sumcheck::{coefficient_sumcheck, RoundPolyEvaluator}, folding::protogalaxy, hypercube::{compute_hypercube_eq_evals, Hypercube}, inner_product_sumcheck, @@ -74,56 +74,64 @@ fn eval_r1cs_constraint_poly( DensePolynomial::from_coefficients_vec(vec![a0 * b0 - c0, a0 * b1 + a1 * b0 - c1, a1 * b1]) } -/// Compute the twin-constraint round polynomial `h(X)`. +/// Evaluator for the twin-constraint sumcheck round polynomial. /// /// Proves `∑_i τ(i) · (f(i) + ω · p(i)) = 0` via protogalaxy folding, where: /// - `f(X)` = `fold(α, oracle_evals)` — folded codeword check /// - `p(X)` = `fold(β, Az·Bz - Cz)` — folded R1CS constraint check /// - `t(X)` = linear interpolation of τ — equality polynomial /// -/// Returns `h(X) = Σ (f(X) + ω·p(X)) · t(X)`. -/// -/// `expected_num_coeffs` is the number of coefficients the verifier expects to read. -/// The result is padded with zeros to ensure the prover always writes exactly that many. -fn twin_constraint_round_poly( - tablewise: &[Vec>], - pairwise: &[Vec], - r1cs: &R1CSConstraints, +/// Each pair contributes `h(X) = (f(X) + ω·p(X)) · t(X)`. +struct TwinConstraintEvaluator<'a, F: Field> { + r1cs: &'a R1CSConstraints, omega: F, - expected_num_coeffs: usize, -) -> DensePolynomial { - let (u, z, a, b) = (&tablewise[0], &tablewise[1], &tablewise[2], &tablewise[3]); - let tau = &pairwise[0]; - - let f_iter = u.chunks(2).zip(a.chunks(2)).map(|(u, a)| { - protogalaxy::fold( - a[0].iter().zip(&a[1]).map(|(&l, &r)| (l, r - l)), - u[0].iter() - .zip(&u[1]) + degree: usize, +} + +impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { + fn degree(&self) -> usize { + self.degree + } + + fn accumulate_pair(&self, coeffs: &mut [F], tw: &[(&[F], &[F])], pw: &[(F, F)]) { + // tw[0] = (u_even, u_odd), tw[1] = (z_even, z_odd), + // tw[2] = (a_even, a_odd), tw[3] = (b_even, b_odd) + // pw[0] = (tau_even, tau_odd) + let (u_even, u_odd) = tw[0]; + let (z_even, z_odd) = tw[1]; + let (a_even, a_odd) = tw[2]; + let (b_even, b_odd) = tw[3]; + let (tau_even, tau_odd) = pw[0]; + + // f(X) = fold(α, oracle_evals): protogalaxy fold over α pairs and linear polys from u + let f = protogalaxy::fold( + a_even.iter().zip(a_odd).map(|(&l, &r)| (l, r - l)), + u_even + .iter() + .zip(u_odd) .map(|(&l, &r)| linear_poly(l, r)) .collect(), - ) - }); - let p_iter = b.chunks(2).zip(z.chunks(2)).map(|(b, z)| { - protogalaxy::fold( - b[0].iter().zip(&b[1]).map(|(&l, &r)| (l, r - l)), - r1cs.iter() - .map(|c| eval_r1cs_constraint_poly(c, &z[0], &z[1])) + ); + + // p(X) = fold(β, Az·Bz - Cz): protogalaxy fold over β pairs and R1CS constraint polys + let p = protogalaxy::fold( + b_even.iter().zip(b_odd).map(|(&l, &r)| (l, r - l)), + self.r1cs + .iter() + .map(|c| eval_r1cs_constraint_poly(c, z_even, z_odd)) .collect(), - ) - }); - let t_iter = tau.chunks(2).map(|t| linear_poly(t[0], t[1])); - - let mut h = f_iter - .zip(p_iter) - .zip(t_iter) - .map(|((f, p), t)| (f + p * omega).naive_mul(&t)) - .fold(DensePolynomial::zero(), |acc, r| acc + r); - - // Pad coefficients to the expected count so the prover writes exactly - // as many field elements as the verifier reads in derive_randomness. - h.coeffs.resize(expected_num_coeffs, F::zero()); - h + ); + + // t(X) = tau_even + (tau_odd - tau_even) · X + let t = linear_poly(tau_even, tau_odd); + + // h(X) = (f(X) + ω·p(X)) · t(X) + let h = (f + p * self.omega).naive_mul(&t); + + for (c, &hc) in coeffs.iter_mut().zip(h.coeffs.iter()) { + *c += hc; + } + } } pub trait BoolResult { @@ -191,19 +199,24 @@ impl< log_m: usize, ) -> Result, ProverError> { // a. encode witnesses + let t1 = std::time::Instant::now(); let (codewords, leaves) = build_codeword_leaves(&self.params.code, witnesses, l1); + eprintln!("[PROFILE] rs_encode (FFT): {:?}", t1.elapsed()); // b. evaluation claims let mus = codewords.iter().map(|f| f[0]).collect::>(); // c. commit to witnesses + let t1 = std::time::Instant::now(); let td_0 = MerkleTree::::new( &self.params.mt_leaf_hash_params, &self.params.mt_two_to_one_hash_params, leaves.chunks_exact(l1).collect::>(), )?; + eprintln!("[PROFILE] pesat_merkle_tree: {:?}", t1.elapsed()); // d. absorb commitment and code evaluations + let t1 = std::time::Instant::now(); let root_bytes: [u8; 32] = td_0 .root() .as_ref() @@ -216,6 +229,10 @@ impl< let taus = (0..l1) .map(|_| prover_state.verifier_messages_vec::(log_m)) .collect::>(); + eprintln!( + "[PROFILE] pesat_absorb + transcript: {:?}", + t1.elapsed() + ); Ok(PesatOutput { codewords, @@ -253,6 +270,11 @@ impl< let log_m = log2(M) as usize; let l1 = pesat.codewords.len(); + let _t_total = std::time::Instant::now(); + eprintln!( + "[PROFILE] M={M}, N={N}, k={k}, n(code_len)={n}, log_n={log_n}, log_l={log_l}, l1={l1}" + ); + // a. zero check randomness let omega: F = prover_state.verifier_message(); let tau = prover_state.verifier_messages_vec::(log_l); @@ -287,15 +309,16 @@ impl< let mut pw = [tau_eq_evals]; // tau let r1cs = self.params.p.constraints(); - let expected_num_coeffs = 2 + (log_n + 1).max(log_m + 2); - let sc = coefficient_sumcheck( - |tw, pw| twin_constraint_round_poly(tw, pw, r1cs, omega, expected_num_coeffs), - &mut tablewise, - &mut pw, - log_l, - prover_state, - ); + let degree = 1 + (log_n + 1).max(log_m + 2); + let evaluator = TwinConstraintEvaluator { + r1cs, + omega, + degree, + }; + let t1 = std::time::Instant::now(); + let sc = coefficient_sumcheck(&evaluator, &mut tablewise, &mut pw, log_l, prover_state); let gamma = sc.verifier_messages; + eprintln!("[PROFILE] twin_constraint_sumcheck: {:?}", t1.elapsed()); debug_assert_eq!(gamma.len(), log_l); @@ -307,6 +330,7 @@ impl< let beta_tau = b_red.pop().unwrap(); // eval the bundled r1cs + let t1 = std::time::Instant::now(); let beta_eq_evals = (0..M).map(|i| eq_poly(&beta_tau, i)).collect::>(); let eta = self @@ -320,7 +344,10 @@ impl< let f_hat = DenseMultilinearExtension::from_evaluations_slice(log_n, &f); let nu_0 = f_hat.fix_variables(&zeta_0)[0]; + eprintln!("[PROFILE] eval_bundled_r1cs: {:?}", t1.elapsed()); + // f. new commitment + let t1 = std::time::Instant::now(); let td = MerkleTree::::new( &self.params.mt_leaf_hash_params, &self.params.mt_two_to_one_hash_params, @@ -337,7 +364,10 @@ impl< prover_state.prover_message(&eta); prover_state.prover_message(&nu_0); + eprintln!("[PROFILE] merkle_commit: {:?}", t1.elapsed()); + // h. ood samples + let t1 = std::time::Instant::now(); let n_ood_samples = self.params.config.s * log_n; let ood_samples = prover_state.verifier_messages_vec::(n_ood_samples); let ood_samples = ood_samples.chunks(log_n).collect::>(); @@ -347,8 +377,10 @@ impl< .iter() .map(|ood_p| f_hat.fix_variables(ood_p)[0]) .collect::>(); + eprintln!("[PROFILE] ood fix_variables: {:?}", t1.elapsed()); // j. absorb ood answers + let t1 = std::time::Instant::now(); prover_state.prover_messages(&ood_answers); let mut zetas = vec![zeta_0.as_slice()]; @@ -360,6 +392,9 @@ impl< let r = 1 + self.params.config.s + self.params.config.t; let log_r = log2(r) as usize; let queries = QueryIndices::sample(prover_state, log_n, self.params.config.t); + eprintln!("[PROFILE] query_sampling: {:?}", t1.elapsed()); + + let t1 = std::time::Instant::now(); let xis = prover_state.verifier_messages_vec(log_r); zetas.extend(queries.evaluation_points.iter().map(|v| v.as_slice())); @@ -377,7 +412,13 @@ impl< }) .collect::>(); + eprintln!( + "[PROFILE] eq_poly_evals + ood_evals_vec: {:?}", + t1.elapsed() + ); + // [CBBZ23] optimization from hyperplonk + let t1 = std::time::Instant::now(); let id_non_0_eval_sums = accumulate_sparse_evaluations(zetas, xi_eq_evals, self.params.config.s, r); @@ -389,10 +430,16 @@ impl< ) .verifier_messages; + eprintln!( + "[PROFILE] batching_sumcheck (inner product): {:?}", + t1.elapsed() + ); + // m. new target let mu = f_hat.fix_variables(&alpha)[0]; // n. compute authentication paths + let t1 = std::time::Instant::now(); let auth_0 = compute_auth_paths(&pesat.td_0, &queries.leaf_positions)?; let auth = acc_witness @@ -414,6 +461,8 @@ impl< shift_queries_answers[i] = answers; } + eprintln!("[PROFILE] auth_paths + shift_queries: {:?}", t1.elapsed()); + let new_acc_instance = AccumulatorInstance { rt: vec![td.root()], alpha: vec![alpha], @@ -507,12 +556,15 @@ impl< //////////////////////// // 2. PESAT Reduction //////////////////////// + let t0 = std::time::Instant::now(); let pesat = self.pesat_reduce(prover_state, &witnesses, l1, log_m)?; + eprintln!("[PROFILE] pesat_reduce: {:?}", t0.elapsed()); //////////////////////// // 3. Constrained Code Accumulation //////////////////////// - self.constrained_code_accumulate( + let t0 = std::time::Instant::now(); + let result = self.constrained_code_accumulate( prover_state, pesat, &instances, @@ -523,7 +575,9 @@ impl< N, k, log_l, - ) + ); + eprintln!("[PROFILE] constrained_code_accumulate: {:?}", t0.elapsed()); + result } fn verify<'a>( @@ -698,10 +752,12 @@ impl< (coeffs_twinc_sumcheck.len() == log_l).ok_or_err(VerifierError::NumSumcheckRounds)?; let mut target_1 = sigma_1; - for (coeffs, gamma) in coeffs_twinc_sumcheck.into_iter().zip(&gamma_sumcheck) { + for (mut coeffs, gamma) in coeffs_twinc_sumcheck.into_iter().zip(&gamma_sumcheck) { + // Derive leading coefficient: c_d = claim - 2*c_0 - c_1 - ... - c_{d-1} + let partial_sum: F = coeffs.iter().skip(1).copied().sum(); + let leading = target_1 - coeffs[0].double() - partial_sum; + coeffs.push(leading); let h = DensePolynomial::from_coefficients_vec(coeffs); - (h.evaluate(&F::one()) + h.evaluate(&F::zero()) == target_1) - .ok_or_err(VerifierError::SumcheckRound)?; target_1 = h.evaluate(gamma); } diff --git a/src/protocol/transcript/verifier.rs b/src/protocol/transcript/verifier.rs index 19ae9c6..36e58d4 100644 --- a/src/protocol/transcript/verifier.rs +++ b/src/protocol/transcript/verifier.rs @@ -133,7 +133,7 @@ pub fn derive_randomness< let mut coeffs_twinc_sumcheck = Vec::new(); for _ in 0..log_l { let h_coeffs: Vec = - verifier_state.prover_messages_vec(2 + (log_n + 1).max(log_m + 2))?; + verifier_state.prover_messages_vec(1 + (log_n + 1).max(log_m + 2))?; let c: F = verifier_state.verifier_message(); gamma_sumcheck.push(c); coeffs_twinc_sumcheck.push(h_coeffs); From 5bf67533c4aa0ab63806a43d98fbfcc3b4138c7d Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Sat, 18 Apr 2026 13:40:05 +0200 Subject: [PATCH 09/68] Plan 0: phase modules + Oracle abstraction + tracing scaffold Extracts WARP::prove and ::verify into per-phase modules under src/protocol/phases/ (pesat, twin_constraint, ood, batching, proximity), with a shared Oracle type in src/protocol/oracle.rs that owns the codeword and a lazily-materialised multilinear extension. lib.rs is now an orchestrator that threads oracles and accumulator state through the phases. Adds tracing spans at each phase boundary, gated behind a new `profile` cargo feature that optionally pulls in tracing-subscriber via src/profile.rs. Release builds do not pull tracing-subscriber as a direct dep of warp. Sets up docs/paper-mods/ as a living spec of notation modifications to the Warp paper, paired 1:1 with Rust modules. mod1_oracle.tex authored in full; mod2/3/4 stubbed for downstream plans. Framing stays inside IOP/BCS rather than moving to AHP. No behavior change: 14/14 tests pass (BLS12-381 + Goldilocks warp_test, query + relation tests), clippy clean under --all-features. --- .gitignore | 3 +- Cargo.toml | 7 + docs/paper-mods/README.md | 40 ++ docs/paper-mods/mod1_oracle.tex | 129 ++++ docs/paper-mods/mod2_structured_sumcheck.tex | 34 + docs/paper-mods/mod3_accumulator_state.tex | 30 + docs/paper-mods/mod4_parameter_selection.tex | 29 + docs/paper-mods/notation.tex | 58 ++ src/lib.rs | 633 +++++-------------- src/profile.rs | 41 ++ src/protocol/mod.rs | 2 + src/protocol/oracle.rs | 78 +++ src/protocol/phases/batching.rs | 112 ++++ src/protocol/phases/mod.rs | 23 + src/protocol/phases/ood.rs | 44 ++ src/protocol/phases/pesat.rs | 89 +++ src/protocol/phases/proximity.rs | 148 +++++ src/protocol/phases/twin_constraint.rs | 242 +++++++ 18 files changed, 1250 insertions(+), 492 deletions(-) create mode 100644 docs/paper-mods/README.md create mode 100644 docs/paper-mods/mod1_oracle.tex create mode 100644 docs/paper-mods/mod2_structured_sumcheck.tex create mode 100644 docs/paper-mods/mod3_accumulator_state.tex create mode 100644 docs/paper-mods/mod4_parameter_selection.tex create mode 100644 docs/paper-mods/notation.tex create mode 100644 src/profile.rs create mode 100644 src/protocol/oracle.rs create mode 100644 src/protocol/phases/batching.rs create mode 100644 src/protocol/phases/mod.rs create mode 100644 src/protocol/phases/ood.rs create mode 100644 src/protocol/phases/pesat.rs create mode 100644 src/protocol/phases/proximity.rs create mode 100644 src/protocol/phases/twin_constraint.rs diff --git a/.gitignore b/.gitignore index 7b4eda9..1857d77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target /Cargo.lock .vscode -.DS_Store \ No newline at end of file +.DS_Store +.claude/settings.local.json \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 1e20d5b..4f9e0f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,8 @@ spongefish = { git = "https://github.com/z-tech/spongefish.git", branch = "small ] } efficient-sumcheck = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git", branch = "z-tech/simd_goldilocks_experimental" } thiserror = "2.0.16" +tracing = { version = "0.1", default-features = false, features = ["std", "attributes"] } +tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter"], optional = true } ark-codes = { git = "https://github.com/dmpierre/ark-codes.git" } @@ -51,6 +53,11 @@ default = ["asm"] asm = ["ark-ff/asm"] +# Turns on a `tracing-subscriber` that emits phase timing and dimension fields +# to stderr in the same spirit as the old `[PROFILE]` eprintln! lines. +# Plan O replaces this with structured JSON + op counters. +profile = ["dep:tracing-subscriber"] + [[bench]] name = "warp_rs" harness = false diff --git a/docs/paper-mods/README.md b/docs/paper-mods/README.md new file mode 100644 index 0000000..7346f59 --- /dev/null +++ b/docs/paper-mods/README.md @@ -0,0 +1,40 @@ +# Warp paper-mods + +Living spec of notation and structural modifications we make to the Warp paper +so that the paper's IOR decomposition and the Rust implementation agree +exactly. This is **not** a paper in its own right — it is a working document +tracking deltas from the published construction, each paired with the code +module that realises it. + +## Scope + +Warp is framed as an **IOP** (Ben-Sasson–Chiesa–Spooner 2016), **not** as an +AHP. Oracles are functions `f: [n] → F` queried by index under the BCS +compiler, with multilinear-extension semantics layered on top for point +queries. See `notation.tex` for the shared preamble and rationale. + +## Convention + +- One `.tex` file per modification, paired with one Rust module. +- Shared preamble lives in `notation.tex`; every `.tex` file `\input`s it. +- **Authoring order**: the `.tex` file is written *before* the code module. + Code doc comments cite the `.tex` filename; `.tex` files cite the Rust + module path. Drift is caught in code review. +- No CI compilation. Build locally with `latexmk -pdf mod1_oracle.tex`. + +## Modification numbering (reserved) + +| File | Modification | Owning plan | +|-----------------------------------|------------------------------------------|-------------| +| `mod1_oracle.tex` | Oracle as first-class IOR output | Plan 0 | +| `mod2_structured_sumcheck.tex` | Structured-sumcheck primitive | Plan B' | +| `mod3_accumulator_state.tex` | Accumulator as explicit IOR state | Plan C | +| `mod4_parameter_selection.tex` | Soundness-driven parameter selection | Plan P | + +Future modifications take the next free number in sequence. + +## Cross-reference lint + +Each phase module's doc comment must name a `.tex` file in this folder. Each +`.tex` must name at least one Rust module path it pairs with. A manual check +is in the Plan 0 verification list; automation is Plan T's job. diff --git a/docs/paper-mods/mod1_oracle.tex b/docs/paper-mods/mod1_oracle.tex new file mode 100644 index 0000000..8101507 --- /dev/null +++ b/docs/paper-mods/mod1_oracle.tex @@ -0,0 +1,129 @@ +\documentclass{article} +\input{notation.tex} + +\title{Modification 1: \\ Oracle as first-class IOR output} +\author{Warp paper-mods} +\date{} + +\begin{document} +\maketitle + +\paragraph{Paired Rust module.} \codemod{src/protocol/oracle.rs}, consumed +throughout \codemod{src/protocol/phases/}. + +\section{Motivation} + +The Warp paper decomposes its construction as a sequence of interactive +oracle reductions (IORs). In the pure IOR formalism, each IOR's prover is an +abstract Turing machine that recomputes anything it needs; there is no notion +of ``shared state'' across IORs beyond the transcript. This abstraction is +sound but imposes an unpleasant cost on any implementation: several distinct +IORs in Warp --- the twin-constraint sumcheck, OOD sampling, the batching +sumcheck, and the proximity-query phase --- all operate on \emph{the same +codeword} \(f\), its multilinear extension \(\MLE{f}\), and its Merkle +commitment. A literal reading of the paper forces the code either to +rematerialise these objects phase by phase (wasteful) or to smuggle them +across phase boundaries as ambient state (unprincipled). + +The BCS formalism already provides the right object to legitimise the +sharing: an \emph{oracle message}. BCS treats prover oracles as typed, +persistent objects that can be queried in any subsequent round. What the +paper presently leaves implicit is that \emph{composed} IORs can export and +import these oracles as typed inputs and outputs. + +This modification promotes Oracle to a first-class input/output type of every +Warp IOR. No new soundness argument is required; the BCS compiler already +handles the Merkle-commitment bookkeeping. What changes is the presentation, +which in turn legitimises a direct Rust implementation in +\codemod{src/protocol/oracle.rs}. + +\section{Definition} + +An \emph{oracle} \(\Oracle{f}\) carries the following views of a single +object: +\begin{itemize} + \item \(f : \idx{n} \to \F\) is an evaluation table (the \emph{BCS-native} + view); + \item \(\MLE{f} : \F^{\log n} \to \F\) is the unique multilinear polynomial + over \(\bool{\log n}\) agreeing with \(f\) after identifying + \(\idx{n} \leftrightarrow \bool{\log n}\) by binary expansion; + \item under BCS compilation, \(f\) is committed via a Merkle root + \(\Commit{f}\); this commitment is produced by a separate + \emph{commit} operation and is not a field of \(\Oracle{f}\). +\end{itemize} + +\(\MLE{f}\) is \emph{implied} by \(f\); the prover materialises it lazily and +caches. See \codemod{src/protocol/oracle.rs}, method \texttt{query\_at\_point}. + +\paragraph{Implementation note: commitments and oracles are not 1:1.} Warp's +\Pesat{} phase interleaves \(l_1\) codewords into a single Merkle tree +(\codemod{src/crypto/merkle/mod.rs}, \texttt{build\_codeword\_leaves}), so one +commitment covers many oracles. The Rust \codemod{Oracle} struct therefore +stores only \(f\) and the lazy \(\MLE{f}\); the Merkle tree implementing +\(\Commit{f}\) is tracked by the enclosing data structure +(\codemod{src/types.rs} --- \texttt{PesatOutput}, \texttt{AccumulatorWitness}). +This is an orthogonal implementation choice; the IOR composition rule in +\S4 is unchanged. + +\section{Query interfaces} + +Two legal query operations on \(\Oracle{f}\): + +\begin{description} + \item[Index query.] \(f[i]\) for \(i \in \idx{n}\). BCS-native. + The verifier receives \(f[i]\) alongside a Merkle opening against + \(\Commit{f}\); the BCS compiler makes this non-interactive via + Fiat--Shamir. Corresponds to + \codemod{src/protocol/oracle.rs}, method \texttt{query\_at\_leaf}. + \item[Point query.] \(\MLE{f}(\zeta)\) for \(\zeta \in \F^{\log n}\). + The prover answers directly; the verifier's check that the answer is + consistent with \(\Commit{f}\) is deferred to a downstream IOR (in + Warp, the batching sumcheck). Corresponds to + \codemod{src/protocol/oracle.rs}, method \texttt{query\_at\_point}. +\end{description} + +Both operations act on \emph{the same} oracle; a downstream IOR is free to +mix query kinds. + +\section{Composition rule} + +If IOR \(A\) has signature +\(\; (\text{stmt}_A, \text{wit}_A) \;\mapsto\; (\text{stmt}_A', \Oracle{f})\) +and IOR \(B\) has signature +\(\; (\text{stmt}_B, \Oracle{f}) \;\mapsto\; \text{stmt}_B'\), +then the sequential composition \(A; B\) has signature +\(\; (\text{stmt}_A, \text{stmt}_B, \text{wit}_A) \;\mapsto\; (\text{stmt}_A', \text{stmt}_B') \). +Soundness follows from the BCS composition theorem for oracle messages: the +Merkle commitment \(\Commit{f}\) is produced once by \(A\) and reused by any +number of downstream IORs. + +\section{Application to Warp} + +Every Warp phase can be restated in terms of oracles: + +\begin{center} +\begin{tabular}{l l} +\toprule +Phase & Oracle role \\ +\midrule +Encode-and-commit & emits \(\Oracle{f}\) per fresh witness \\ +\Pesat & emits \(\Oracle{u}\) for accumulated + fresh codewords \\ +\TwinConstraint & consumes \(\Oracle{u}\), emits reduced claim \\ +\OOD & point queries on \(\Oracle{f}\) \\ +\Batching & consumes \(\Oracle{f}\); emits new \(\Oracle{f}'\) via fold \\ +\Proximity & index queries on \(\Oracle{f}\) with auth paths \\ +\bottomrule +\end{tabular} +\end{center} + +The same object carries the codeword, the multilinear extension, and the +Merkle tree; downstream phases access whichever view the operation requires. +In Rust this is literally one struct (\codemod{src/protocol/oracle.rs}). + +\section{What this does not change} + +Soundness proofs, parameter choices, and the transcript layout are unchanged. +Modification 1 is presentational: it makes the existing data flow type-check +in a paper that stays inside BCS rather than retreating to AHP. + +\end{document} diff --git a/docs/paper-mods/mod2_structured_sumcheck.tex b/docs/paper-mods/mod2_structured_sumcheck.tex new file mode 100644 index 0000000..c2128c7 --- /dev/null +++ b/docs/paper-mods/mod2_structured_sumcheck.tex @@ -0,0 +1,34 @@ +\documentclass{article} +\input{notation.tex} + +\title{Modification 2: \\ Structured-sumcheck primitive (stub)} +\author{Warp paper-mods} +\date{} + +\begin{document} +\maketitle + +\paragraph{Status.} Stub. Full spec deferred to Plan B' in the multi-plan +roadmap. Paired code: refactor of +\codemod{src/protocol/phases/twin_constraint.rs} once authored. + +\section*{Abstract} + +Warp's twin-constraint sumcheck reduces the claim +\(\sum_i \tau(i) \cdot (f(i) + \omega \cdot p(i)) = 0\) +where \(f\) and \(p\) are themselves defined as protogalaxy folds over +\(\alpha\) and \(\beta\), respectively. At the paper level this is presented +as a single \Sumcheck{} IOR; at the code level, the prover fuses the folds +across rounds to share passes over tables, which is what lets the phase +dominate only \(\sim 55\%\) of prover time rather than \(\sim 4\times\) +that under the naive sequencing. + +Modification 2 promotes the structure \emph{sum of products of linear +folds} to a first-class IOR primitive in the paper, in the style of +HyperPlonk's \emph{ZeroCheck}. Both Warp's twin-constraint sumcheck and +the batching sumcheck become instances. The primitive justifies the +fusion at the paper level; the code then exposes a +\texttt{StructuredSumcheck} trait whose instances correspond to the +paper's variants. See Plan B' for the full treatment. + +\end{document} diff --git a/docs/paper-mods/mod3_accumulator_state.tex b/docs/paper-mods/mod3_accumulator_state.tex new file mode 100644 index 0000000..dddc6d4 --- /dev/null +++ b/docs/paper-mods/mod3_accumulator_state.tex @@ -0,0 +1,30 @@ +\documentclass{article} +\input{notation.tex} + +\title{Modification 3: \\ Accumulator as explicit IOR state (stub)} +\author{Warp paper-mods} +\date{} + +\begin{document} +\maketitle + +\paragraph{Status.} Stub. Full spec deferred to Plan C in the multi-plan +roadmap. Paired code: signature-level refactor across +\codemod{src/protocol/phases/} once authored. + +\section*{Abstract} + +The paper presently treats the accumulator \((\AccX, \AccW)\) partly as an +object the protocol operates on and partly as ambient state read and written +by each phase. Modification 3 makes the accumulator an explicit pre- and +post-condition of every IOR: +\[ + \IOR{Phase}\ :\ (\AccState, \text{input}) \;\longmapsto\; (\AccState', \text{output}). +\] +This matches Nova-style folding-scheme presentation and lets the composition +theorem be stated mechanically. At the code level it gives every phase +function the same \texttt{AccState} parameter and return and makes prover +and verifier phases exact structural duals. See Plan C for the full +treatment. + +\end{document} diff --git a/docs/paper-mods/mod4_parameter_selection.tex b/docs/paper-mods/mod4_parameter_selection.tex new file mode 100644 index 0000000..f755d99 --- /dev/null +++ b/docs/paper-mods/mod4_parameter_selection.tex @@ -0,0 +1,29 @@ +\documentclass{article} +\input{notation.tex} + +\title{Modification 4: \\ Soundness-driven parameter selection (stub)} +\author{Warp paper-mods} +\date{} + +\begin{document} +\maketitle + +\paragraph{Status.} Stub. Full spec authored in Plan P. Paired code: +\codemod{src/params/} (to be created). + +\section*{Abstract} + +The paper states security bounds symbolically; the implementation currently +hard-codes parameter values (e.g.\ \(s = 8\), \(t = 7\)) with no derivation +record. Modification 4 introduces, both in the paper and in code, an +\emph{inverse} to the soundness analysis: given a security target +\(\lambda\), a field \(\F\), a code rate \(\rho\), and an accumulation +depth, produce the minimum-cost parameter tuple \((s, t, l, l_1)\) hitting +that target. Both list-decoding regimes (provable and conjectured) are +supported via a user toggle. + +The code module \codemod{src/params/} will expose pure \texttt{select} and +\texttt{validate} functions plus a preset table to replace the ad-hoc +constants currently appearing in tests. See Plan P for the full treatment. + +\end{document} diff --git a/docs/paper-mods/notation.tex b/docs/paper-mods/notation.tex new file mode 100644 index 0000000..de81c81 --- /dev/null +++ b/docs/paper-mods/notation.tex @@ -0,0 +1,58 @@ +% notation.tex --- shared preamble for Warp paper-mods. +% +% Every mod*.tex file begins with: +% +% \input{notation.tex} +% +% Conventions +% ----------- +% * Framing: IOP in the sense of Ben-Sasson--Chiesa--Spooner (BCS, TCC 2016), +% NOT Algebraic Holographic Proofs. Oracles are functions $f:[n] \to \F$, +% queried by index; multilinear-extension semantics layer on top for point +% queries. +% * Every modification file cites the Rust module path it pairs with. + +\usepackage{amsmath,amssymb,amsthm} +\usepackage{mathtools} +\usepackage{hyperref} + +% ---- Fields, indexing, domains ------------------------------------------- +\newcommand{\F}{\mathbb{F}} +\newcommand{\idx}[1]{\ensuremath{[#1]}} % index set [n] +\newcommand{\bool}[1]{\ensuremath{\{0,1\}^{#1}}} % boolean hypercube +\newcommand{\size}[1]{\ensuremath{\left\lvert#1\right\rvert}} + +% ---- Oracles (Modification 1) -------------------------------------------- +% An Oracle carries three views of the same object: +% (i) an evaluation table f : [n] -> F (BCS-native) +% (ii) a multilinear extension \hat f : F^{log n} -> F +% (iii) a commitment handle (Merkle root under BCS compilation) +% See mod1_oracle.tex. +\newcommand{\Oracle}[1]{\ensuremath{\mathsf{O}[#1]}} +\newcommand{\MLE}[1]{\ensuremath{\widehat{#1}}} +\newcommand{\Commit}[1]{\ensuremath{\mathsf{com}(#1)}} +\newcommand{\query}{\ensuremath{\mathsf{query}}} % generic query operator + +% ---- Accumulator state (Modification 3, reserved) ------------------------- +\newcommand{\AccX}{\ensuremath{\mathsf{acc}_x}} % accumulator instance +\newcommand{\AccW}{\ensuremath{\mathsf{acc}_w}} % accumulator witness +\newcommand{\AccState}{\ensuremath{(\AccX, \AccW)}} + +% ---- Common structural IORs ---------------------------------------------- +\newcommand{\IOR}[1]{\textsc{#1}} +\newcommand{\Zerocheck}{\IOR{Zerocheck}} +\newcommand{\Sumcheck}{\IOR{Sumcheck}} +\newcommand{\Pesat}{\IOR{Pesat}} +\newcommand{\TwinConstraint}{\IOR{TwinConstraint}} +\newcommand{\Proximity}{\IOR{Proximity}} +\newcommand{\Batching}{\IOR{Batching}} +\newcommand{\OOD}{\IOR{OOD}} + +% ---- Transcript notation -------------------------------------------------- +\newcommand{\absorb}{\ensuremath{\lhd}} % absorb into transcript +\newcommand{\squeeze}{\ensuremath{\rhd}} % derive from transcript + +% ---- Code / module cross-reference --------------------------------------- +% Use \codemod{src/protocol/oracle.rs} in .tex files to point at a Rust +% module. Renders as inline typewriter with a URL when compiled with hyperref. +\newcommand{\codemod}[1]{\href{../../#1}{\texttt{#1}}} diff --git a/src/lib.rs b/src/lib.rs index fa4ae5a..82e08a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,29 +1,18 @@ #![allow(clippy::assign_op_pattern)] // generated by SmallFpConfig derive macro use crate::error::WARPError; use crate::traits::AccumulationScheme; -use crate::types::{ - AccumulatorInstance, AccumulatorWitness, PesatOutput, ProveResult, WARPParams, WARPProof, -}; +use crate::types::{AccumulatorInstance, AccumulatorWitness, ProveResult, WARPParams, WARPProof}; use ark_codes::traits::LinearCode; use ark_crypto_primitives::{ crh::{CRHScheme, TwoToOneCRHScheme}, - merkle_tree::{Config, MerkleTree, Path}, + merkle_tree::{Config, MerkleTree}, }; use ark_ff::{Field, PrimeField}; -use ark_poly::{ - univariate::DensePolynomial, DenseMultilinearExtension, DenseUVPolynomial, - MultilinearExtension, Polynomial, -}; +use ark_poly::{DenseMultilinearExtension, Polynomial}; use ark_std::log2; use config::WARPConfig; -use crypto::merkle::build_codeword_leaves; -use crypto::merkle::compute_auth_paths; use efficient_sumcheck::{ - accumulate_sparse_evaluations, batched_constraint_poly, - coefficient_sumcheck::{coefficient_sumcheck, RoundPolyEvaluator}, - folding::protogalaxy, hypercube::{compute_hypercube_eq_evals, Hypercube}, - inner_product_sumcheck, order_strategy::AscendingOrder, }; use protocol::query::QueryIndices; @@ -43,6 +32,7 @@ pub mod config; pub mod constraints; pub mod crypto; pub mod error; +pub mod profile; pub mod protocol; pub mod relations; pub mod serialize; @@ -50,89 +40,8 @@ pub mod traits; pub mod types; pub mod utils; -use ark_crypto_primitives::Error; use error::{DeciderError, ProverError, VerifierError}; - -/// Degree-1 polynomial interpolating two field elements: `lo + (hi - lo)·X`. -fn linear_poly(lo: F, hi: F) -> DensePolynomial { - DensePolynomial::from_coefficients_vec(vec![lo, hi - lo]) -} - -/// A single R1CS constraint row: sparse representations of A, B, and C. -type R1CSConstraint = (Vec<(F, usize)>, Vec<(F, usize)>, Vec<(F, usize)>); - -/// Evaluate one R1CS constraint `Az·Bz - Cz` as a degree-2 polynomial -/// from two witness vectors `z0`, `z1`. -fn eval_r1cs_constraint_poly( - (a, b, c): &R1CSConstraint, - z0: &[F], - z1: &[F], -) -> DensePolynomial { - let eval = |lc: &[(F, usize)], z: &[F]| lc.iter().map(|(t, i)| z[*i] * t).sum::(); - let (a0, b0, c0) = (eval(a, z0), eval(b, z0), eval(c, z0)); - let (a1, b1, c1) = (eval(a, z1) - a0, eval(b, z1) - b0, eval(c, z1) - c0); - DensePolynomial::from_coefficients_vec(vec![a0 * b0 - c0, a0 * b1 + a1 * b0 - c1, a1 * b1]) -} - -/// Evaluator for the twin-constraint sumcheck round polynomial. -/// -/// Proves `∑_i τ(i) · (f(i) + ω · p(i)) = 0` via protogalaxy folding, where: -/// - `f(X)` = `fold(α, oracle_evals)` — folded codeword check -/// - `p(X)` = `fold(β, Az·Bz - Cz)` — folded R1CS constraint check -/// - `t(X)` = linear interpolation of τ — equality polynomial -/// -/// Each pair contributes `h(X) = (f(X) + ω·p(X)) · t(X)`. -struct TwinConstraintEvaluator<'a, F: Field> { - r1cs: &'a R1CSConstraints, - omega: F, - degree: usize, -} - -impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { - fn degree(&self) -> usize { - self.degree - } - - fn accumulate_pair(&self, coeffs: &mut [F], tw: &[(&[F], &[F])], pw: &[(F, F)]) { - // tw[0] = (u_even, u_odd), tw[1] = (z_even, z_odd), - // tw[2] = (a_even, a_odd), tw[3] = (b_even, b_odd) - // pw[0] = (tau_even, tau_odd) - let (u_even, u_odd) = tw[0]; - let (z_even, z_odd) = tw[1]; - let (a_even, a_odd) = tw[2]; - let (b_even, b_odd) = tw[3]; - let (tau_even, tau_odd) = pw[0]; - - // f(X) = fold(α, oracle_evals): protogalaxy fold over α pairs and linear polys from u - let f = protogalaxy::fold( - a_even.iter().zip(a_odd).map(|(&l, &r)| (l, r - l)), - u_even - .iter() - .zip(u_odd) - .map(|(&l, &r)| linear_poly(l, r)) - .collect(), - ); - - // p(X) = fold(β, Az·Bz - Cz): protogalaxy fold over β pairs and R1CS constraint polys - let p = protogalaxy::fold( - b_even.iter().zip(b_odd).map(|(&l, &r)| (l, r - l)), - self.r1cs - .iter() - .map(|c| eval_r1cs_constraint_poly(c, z_even, z_odd)) - .collect(), - ); - - // t(X) = tau_even + (tau_odd - tau_even) · X - let t = linear_poly(tau_even, tau_odd); - - // h(X) = (f(X) + ω·p(X)) · t(X) - let h = (f + p * self.omega).naive_mul(&t); - - for (c, &hc) in coeffs.iter_mut().zip(h.coeffs.iter()) { - *c += hc; - } - } -} +use protocol::phases::{batching, ood, pesat, proximity, twin_constraint}; pub trait BoolResult { fn ok_or_err(self, err: E) -> Result<(), E>; @@ -185,176 +94,115 @@ impl< P: Clone + BundledPESAT, Config = (usize, usize, usize)>, // m, n, k C: LinearCode + Clone, MT: Config + From<[u8; 32]>>, - > WARP + > AccumulationScheme for WARP { - /// Phase 2: PESAT Reduction - /// - /// Encodes fresh witnesses into codewords, commits via Merkle tree, - /// absorbs the commitment and code evaluations, and derives τ challenges. - fn pesat_reduce( - &self, - prover_state: &mut ProverState, - witnesses: &[Vec], - l1: usize, - log_m: usize, - ) -> Result, ProverError> { - // a. encode witnesses - let t1 = std::time::Instant::now(); - let (codewords, leaves) = build_codeword_leaves(&self.params.code, witnesses, l1); - eprintln!("[PROFILE] rs_encode (FFT): {:?}", t1.elapsed()); - - // b. evaluation claims - let mus = codewords.iter().map(|f| f[0]).collect::>(); - - // c. commit to witnesses - let t1 = std::time::Instant::now(); - let td_0 = MerkleTree::::new( - &self.params.mt_leaf_hash_params, - &self.params.mt_two_to_one_hash_params, - leaves.chunks_exact(l1).collect::>(), - )?; - eprintln!("[PROFILE] pesat_merkle_tree: {:?}", t1.elapsed()); - - // d. absorb commitment and code evaluations - let t1 = std::time::Instant::now(); - let root_bytes: [u8; 32] = td_0 - .root() - .as_ref() - .try_into() - .expect("root must be 32 bytes"); - prover_state.prover_message(&root_bytes); - prover_state.prover_messages(&mus); - - // e. zero check randomness and f. bundled evaluations - let taus = (0..l1) - .map(|_| prover_state.verifier_messages_vec::(log_m)) - .collect::>(); - eprintln!( - "[PROFILE] pesat_absorb + transcript: {:?}", - t1.elapsed() - ); + type Index = P; + type ProverKey = (P, usize, usize, usize); + type VerifierKey = (usize, usize, usize); + type Instances = Vec>; + type Witnesses = Vec>; - Ok(PesatOutput { - codewords, - td_0, - mus, - taus, - }) + fn index( + prover_state: &mut ProverState, + index: Self::Index, + ) -> spongefish::VerificationResult<(Self::ProverKey, Self::VerifierKey)> { + let (m, n, k) = index.config(); + // initialize prover state for fs + // TODO for R1CS + prover_state.public_message(&index.description()); + prover_state.prover_message(&F::from(m as u32)); + prover_state.prover_message(&F::from(n as u32)); + prover_state.prover_message(&F::from(k as u32)); + Ok(((index, m, n, k), (m, n, k))) } - /// Phase 3: Constrained Code Accumulation - /// - /// Combines PESAT output with accumulated state, runs: - /// - Twin constraint sumcheck - /// - OOD sampling - /// - Batching sumcheck - /// - Authentication path computation - /// - /// Returns the new accumulator and proof. - #[allow(clippy::too_many_arguments)] - fn constrained_code_accumulate( + #[tracing::instrument(name = "warp.prove", skip_all)] + fn prove( &self, + pk: Self::ProverKey, prover_state: &mut ProverState, - pesat: PesatOutput, - instances: &[Vec], - witnesses: &[Vec], + witnesses: Self::Witnesses, + instances: Self::Instances, acc_instance: AccumulatorInstance, acc_witness: AccumulatorWitness, - #[allow(non_snake_case)] M: usize, - #[allow(non_snake_case)] N: usize, - k: usize, - log_l: usize, ) -> ProveResult { - let n = self.params.code.code_len(); - let log_n = log2(n) as usize; - let log_m = log2(M) as usize; - let l1 = pesat.codewords.len(); - - let _t_total = std::time::Instant::now(); - eprintln!( - "[PROFILE] M={M}, N={N}, k={k}, n(code_len)={n}, log_n={log_n}, log_l={log_l}, l1={l1}" - ); - - // a. zero check randomness - let omega: F = prover_state.verifier_message(); - let tau = prover_state.verifier_messages_vec::(log_l); + debug_assert!(instances.len() > 1); + debug_assert_eq!(witnesses.len(), instances.len()); + debug_assert_eq!(acc_witness.td.len(), acc_instance.rt.len()); - // b. define [...] - // c. sumcheck protocol - let tau_eq_evals = Hypercube::::new(log_l) - .map(|(index, _point)| eq_poly(&tau, index)) - .collect::>(); + let (l1, l) = (self.params.config.l1, self.params.config.l); + let l2 = l - l1; + debug_assert_eq!(l1 + l2, l); + debug_assert!(l.is_power_of_two()); - let alpha_vecs = concat_slices(&acc_instance.alpha, &vec![vec![F::zero(); log_n]; l1]); + // Parse phase: dimensions and transcript priming. + #[allow(non_snake_case)] + let (M, N, k) = (pk.1, pk.2, pk.3); + let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); + let log_n = log2(self.params.code.code_len()) as usize; - // build the z (x, w) vectors - let z_vecs: Vec> = acc_instance - .beta - .1 - .iter() - .zip(&acc_witness.w) - .chain(instances.iter().zip(witnesses)) - .map(|(x, w)| concat_slices(x, w)) - .collect(); - - let beta_vecs: Vec> = acc_instance.beta.0.into_iter().chain(pesat.taus).collect(); - - // Twin Constraint sumcheck - let mut tablewise = [ - concat_slices(&acc_witness.f, &pesat.codewords), // u - z_vecs, // z - alpha_vecs, // a - beta_vecs, // b - ]; - let mut pw = [tau_eq_evals]; // tau - - let r1cs = self.params.p.constraints(); - let degree = 1 + (log_n + 1).max(log_m + 2); - let evaluator = TwinConstraintEvaluator { - r1cs, - omega, - degree, - }; - let t1 = std::time::Instant::now(); - let sc = coefficient_sumcheck(&evaluator, &mut tablewise, &mut pw, log_l, prover_state); - let gamma = sc.verifier_messages; - eprintln!("[PROFILE] twin_constraint_sumcheck: {:?}", t1.elapsed()); + debug_assert_eq!(instances[0].len(), N - k); + absorb_instances(prover_state, &instances); + acc_instance.absorb_into(prover_state); - debug_assert_eq!(gamma.len(), log_l); + // Destructure acc_witness so we can hand .f/.w to twin_constraint by + // reference and move .td/.f into proximity afterwards. + let AccumulatorWitness { + td: acc_tds, + f: acc_fs, + w: acc_ws, + } = acc_witness; - // e. new oracle and target — after log_l rounds each group has one table left - let [mut u_red, mut z_red, mut a_red, mut b_red] = tablewise; - let f = u_red.pop().unwrap(); - let z = z_red.pop().unwrap(); - let zeta_0 = a_red.pop().unwrap(); - let beta_tau = b_red.pop().unwrap(); + // Phase 2: PESAT — emit oracles (codewords), commit, squeeze τs. + let pesat = pesat::prove::( + prover_state, + &self.params.code, + &self.params.mt_leaf_hash_params, + &self.params.mt_two_to_one_hash_params, + &witnesses, + l1, + log_m, + )?; - // eval the bundled r1cs - let t1 = std::time::Instant::now(); - let beta_eq_evals = (0..M).map(|i| eq_poly(&beta_tau, i)).collect::>(); + // Phase 3a: twin-constraint sumcheck. + let tc = twin_constraint::prove::( + prover_state, + &pesat.codewords, + pesat.taus, + acc_instance, + &acc_fs, + &acc_ws, + &instances, + &witnesses, + self.params.p.constraints(), + log_l, + log_m, + log_n, + ); + // Phase 3b: bundled η, ν₀, new commitment, absorb — the "emit new + // oracle + claims" step between twin-constraint and OOD. + let beta_eq_evals = (0..M).map(|i| eq_poly(&tc.beta_tau, i)).collect::>(); let eta = self .params .p - .evaluate_bundled(&beta_eq_evals, &z) + .evaluate_bundled(&beta_eq_evals, &tc.z) .map_err(|_| ProverError::SpongeFish)?; + let nu_0 = tc.f.query_at_point(&tc.zeta_0); - let (x, w) = z.split_at(N - k); - let beta = (vec![beta_tau], vec![x.to_vec()]); - let f_hat = DenseMultilinearExtension::from_evaluations_slice(log_n, &f); - let nu_0 = f_hat.fix_variables(&zeta_0)[0]; + let (new_x, new_w) = tc.z.split_at(N - k); + let new_x = new_x.to_vec(); + let new_w = new_w.to_vec(); + let new_beta = (vec![tc.beta_tau.clone()], vec![new_x]); - eprintln!("[PROFILE] eval_bundled_r1cs: {:?}", t1.elapsed()); - - // f. new commitment - let t1 = std::time::Instant::now(); - let td = MerkleTree::::new( - &self.params.mt_leaf_hash_params, - &self.params.mt_two_to_one_hash_params, - f.chunks(1).collect::>(), - )?; - - // g. absorb new commitment and target + let td = { + let _s = tracing::info_span!("warp.commit_new_oracle").entered(); + MerkleTree::::new( + &self.params.mt_leaf_hash_params, + &self.params.mt_two_to_one_hash_params, + tc.f.evals().chunks(1).collect::>(), + )? + }; let td_root_bytes: [u8; 32] = td .root() .as_ref() @@ -364,221 +212,64 @@ impl< prover_state.prover_message(&eta); prover_state.prover_message(&nu_0); - eprintln!("[PROFILE] merkle_commit: {:?}", t1.elapsed()); - - // h. ood samples - let t1 = std::time::Instant::now(); - let n_ood_samples = self.params.config.s * log_n; - let ood_samples = prover_state.verifier_messages_vec::(n_ood_samples); - let ood_samples = ood_samples.chunks(log_n).collect::>(); - - // i. ood answers - let ood_answers = ood_samples - .iter() - .map(|ood_p| f_hat.fix_variables(ood_p)[0]) - .collect::>(); - eprintln!("[PROFILE] ood fix_variables: {:?}", t1.elapsed()); - - // j. absorb ood answers - let t1 = std::time::Instant::now(); - prover_state.prover_messages(&ood_answers); + // Phase 3c: OOD — point queries on the oracle. + let ood_out = ood::prove::(prover_state, &tc.f, self.params.config.s, log_n); - let mut zetas = vec![zeta_0.as_slice()]; - let mut nus = vec![nu_0]; - zetas.extend(ood_samples); - nus.extend(ood_answers); - - // k. shift queries and zerocheck randomness - let r = 1 + self.params.config.s + self.params.config.t; - let log_r = log2(r) as usize; - let queries = QueryIndices::sample(prover_state, log_n, self.params.config.t); - eprintln!("[PROFILE] query_sampling: {:?}", t1.elapsed()); - - let t1 = std::time::Instant::now(); - let xis = prover_state.verifier_messages_vec(log_r); + // Sample shift queries — ordering-coupled to the batching ξ below. + let queries = QueryIndices::::sample(prover_state, log_n, self.params.config.t); + // Phase 3d: batching sumcheck. Assemble zetas = [ζ₀, ood_j…, query_k…]. + let ood_chunks: Vec<&[F]> = ood_out.samples_flat.chunks(log_n).collect(); + let mut zetas: Vec<&[F]> = + Vec::with_capacity(1 + self.params.config.s + self.params.config.t); + zetas.push(tc.zeta_0.as_slice()); + zetas.extend(ood_chunks); zetas.extend(queries.evaluation_points.iter().map(|v| v.as_slice())); - // l. sumcheck polynomials - // compute evaluations for xi - - let xi_eq_evals = (0..r).map(|i| eq_poly(&xis, i)).collect::>(); - - let ood_evals_vec = (0..1 + self.params.config.s) - .map(|i| { - (0..n) - .map(|a| eq_poly(zetas[i], a) * xi_eq_evals[i]) - .collect::>() - }) - .collect::>(); - - eprintln!( - "[PROFILE] eq_poly_evals + ood_evals_vec: {:?}", - t1.elapsed() - ); - - // [CBBZ23] optimization from hyperplonk - let t1 = std::time::Instant::now(); - let id_non_0_eval_sums = - accumulate_sparse_evaluations(zetas, xi_eq_evals, self.params.config.s, r); - - // call efficient sumcheck for batched_constraint checks - let alpha = inner_product_sumcheck( - &mut f.clone(), - &mut batched_constraint_poly(&ood_evals_vec, &id_non_0_eval_sums), + let batching_out = batching::prove::( prover_state, - ) - .verifier_messages; - - eprintln!( - "[PROFILE] batching_sumcheck (inner product): {:?}", - t1.elapsed() + &tc.f, + &zetas, + self.params.config.s, + self.params.config.t, + log_n, ); - // m. new target - let mu = f_hat.fix_variables(&alpha)[0]; - - // n. compute authentication paths - let t1 = std::time::Instant::now(); - let auth_0 = compute_auth_paths(&pesat.td_0, &queries.leaf_positions)?; - - let auth = acc_witness - .td // for each accumulated witness and for each - .iter() - .map(|td| compute_auth_paths(td, &queries.leaf_positions)) - .collect::>>, Error>>()?; - - let all_codewords = acc_witness - .f - .into_iter() - .chain(pesat.codewords) - .collect::>(); - - let mut shift_queries_answers = - vec![vec![F::default(); all_codewords.len()]; queries.leaf_positions.len()]; - for (i, idx) in queries.leaf_positions.iter().enumerate() { - let answers = all_codewords.iter().map(|f| f[*idx]).collect::>(); - shift_queries_answers[i] = answers; - } + // Phase 3e: proximity — index queries + auth paths on accumulated + + // fresh oracles (NOT on the reduced `f`, which is this round's new + // oracle). + let all_codewords: Vec> = acc_fs.into_iter().chain(pesat.codewords).collect(); + let prox = proximity::prove::(&queries, &pesat.td_0, &acc_tds, &all_codewords)?; - eprintln!("[PROFILE] auth_paths + shift_queries: {:?}", t1.elapsed()); + // Assemble new accumulator state and proof. + let mut nus = Vec::with_capacity(1 + self.params.config.s); + nus.push(nu_0); + nus.extend(ood_out.answers); let new_acc_instance = AccumulatorInstance { rt: vec![td.root()], - alpha: vec![alpha], - mu: vec![mu], - beta, + alpha: vec![batching_out.alpha], + mu: vec![batching_out.mu], + beta: new_beta, eta: vec![eta], }; let new_acc_witness = AccumulatorWitness { td: vec![td], - f: vec![f], - w: vec![w.to_vec()], + f: vec![tc.f.into_evals()], + w: vec![new_w], }; - let proof = WARPProof { rt_0: pesat.td_0.root(), mu_i: pesat.mus, nu_0, nu_i: nus, - auth_0, - auth_j: auth, - shift_query_answers: shift_queries_answers, + auth_0: prox.auth_0, + auth_j: prox.auth_j, + shift_query_answers: prox.shift_query_answers, }; Ok(((new_acc_instance, new_acc_witness), proof)) } -} - -impl< - F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - P: Clone + BundledPESAT, Config = (usize, usize, usize)>, // m, n, k - C: LinearCode + Clone, - MT: Config + From<[u8; 32]>>, - > AccumulationScheme for WARP -{ - type Index = P; - type ProverKey = (P, usize, usize, usize); - type VerifierKey = (usize, usize, usize); - type Instances = Vec>; - type Witnesses = Vec>; - - fn index( - prover_state: &mut ProverState, - index: Self::Index, - ) -> spongefish::VerificationResult<(Self::ProverKey, Self::VerifierKey)> { - let (m, n, k) = index.config(); - // initialize prover state for fs - // TODO for R1CS - prover_state.public_message(&index.description()); - prover_state.prover_message(&F::from(m as u32)); - prover_state.prover_message(&F::from(n as u32)); - prover_state.prover_message(&F::from(k as u32)); - Ok(((index, m, n, k), (m, n, k))) - } - - fn prove( - &self, - pk: Self::ProverKey, - prover_state: &mut ProverState, - witnesses: Self::Witnesses, - instances: Self::Instances, - acc_instance: AccumulatorInstance, - acc_witness: AccumulatorWitness, - ) -> Result< - ( - (AccumulatorInstance, AccumulatorWitness), - WARPProof, - ), - ProverError, - > { - debug_assert!(instances.len() > 1); - debug_assert_eq!(witnesses.len(), instances.len()); - debug_assert_eq!(acc_witness.td.len(), acc_instance.rt.len()); - - let (l1, l) = (self.params.config.l1, self.params.config.l); - let l2 = l - l1; - debug_assert_eq!(l1 + l2, l); - debug_assert!(l.is_power_of_two()); - - //////////////////////// - // 1. Parsing phase - //////////////////////// - #[allow(non_snake_case)] - let (M, N, k) = (pk.1, pk.2, pk.3); - let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); - - debug_assert_eq!(instances[0].len(), N - k); - - absorb_instances(prover_state, &instances); - acc_instance.absorb_into(prover_state); - - //////////////////////// - // 2. PESAT Reduction - //////////////////////// - let t0 = std::time::Instant::now(); - let pesat = self.pesat_reduce(prover_state, &witnesses, l1, log_m)?; - eprintln!("[PROFILE] pesat_reduce: {:?}", t0.elapsed()); - - //////////////////////// - // 3. Constrained Code Accumulation - //////////////////////// - let t0 = std::time::Instant::now(); - let result = self.constrained_code_accumulate( - prover_state, - pesat, - &instances, - &witnesses, - acc_instance, - acc_witness, - M, - N, - k, - log_l, - ); - eprintln!("[PROFILE] constrained_code_accumulate: {:?}", t0.elapsed()); - result - } fn verify<'a>( &self, @@ -704,70 +395,30 @@ impl< let expected_beta = concat_slices(&acc_instance.beta.0[0], &acc_instance.beta.1[0]); (expected_beta == beta).ok_or_err(VerifierError::CircuitEvaluationPoint)?; - // c. check auth paths + // c. proximity: check auth paths + leaf-position consistency. let queries: QueryIndices = QueryIndices::from_squeezed_bytes(&bytes_shift_queries, log_n, self.params.config.t); - // check: - // that the leaf index corresponds to the shift query - // that the path is correct - (proof.shift_query_answers.len() == self.params.config.t) - .ok_or_err(VerifierError::NumShiftQueries)?; - - // proof.auth_0 - for (i, path) in proof.auth_0.iter().enumerate() { - (path.leaf_index == queries.leaf_positions[i]) - .ok_or_err(VerifierError::ShiftQueryIndex)?; - - let is_valid = path.verify( - &self.params.mt_leaf_hash_params, - &self.params.mt_two_to_one_hash_params, - &rt_0, - &proof.shift_query_answers[i][l2..], // leaves are evaluations of the l1 codewords - )?; - is_valid.ok_or_err(VerifierError::ShiftQuery)? - } - - // proof.auth_j holds merkle proofs for l2 accumulated instances - (proof.auth_j.len() == l2).ok_or_err(VerifierError::NumL2Instances)?; - for (i, paths) in proof.auth_j.iter().enumerate() { - (paths.len() == self.params.config.t).ok_or_err(VerifierError::NumShiftQueries)?; - let root = &l2_roots[i]; - for (j, path) in paths.iter().enumerate() { - (path.leaf_index == queries.leaf_positions[j]) - .ok_or_err(VerifierError::ShiftQueryIndex)?; - let is_valid = path.verify( - &self.params.mt_leaf_hash_params, - &self.params.mt_two_to_one_hash_params, - root, - [proof.shift_query_answers[j][i]], // shift_query_answers[j][i] holds f_i(x_j) - )?; - - is_valid.ok_or_err(VerifierError::ShiftQuery)? - } - } + proximity::verify::( + &queries, + &rt_0, + &l2_roots, + &proof.auth_0, + &proof.auth_j, + &proof.shift_query_answers, + &self.params.mt_leaf_hash_params, + &self.params.mt_two_to_one_hash_params, + l2, + self.params.config.t, + )?; - // d. sumcheck decisions - // twin constraints sumcheck + // d. sumcheck decisions: reduce each transcript's round messages to + // a single target, then compare to the expected claim in (e). (coeffs_twinc_sumcheck.len() == log_l).ok_or_err(VerifierError::NumSumcheckRounds)?; + let target_1 = twin_constraint::verify_claim(sigma_1, coeffs_twinc_sumcheck, &gamma_sumcheck); - let mut target_1 = sigma_1; - for (mut coeffs, gamma) in coeffs_twinc_sumcheck.into_iter().zip(&gamma_sumcheck) { - // Derive leading coefficient: c_d = claim - 2*c_0 - c_1 - ... - c_{d-1} - let partial_sum: F = coeffs.iter().skip(1).copied().sum(); - let leading = target_1 - coeffs[0].double() - partial_sum; - coeffs.push(leading); - let h = DensePolynomial::from_coefficients_vec(coeffs); - target_1 = h.evaluate(gamma); - } - - // multilinear batching sumcheck (sums_batching_sumcheck.len() == log_n).ok_or_err(VerifierError::NumSumcheckRounds)?; - let mut target_2 = sigma_2; - for ([a, b], alpha) in sums_batching_sumcheck.into_iter().zip(&alpha_sumcheck) { - target_2 = - (target_2 - b) * alpha.square() + a * (F::one() - alpha.double()) + b * alpha; - } + let target_2 = batching::verify_claim(sigma_2, sums_batching_sumcheck, &alpha_sumcheck); // e. new target decision // build eq^{\star}(\alpha) diff --git a/src/profile.rs b/src/profile.rs new file mode 100644 index 0000000..51a3b17 --- /dev/null +++ b/src/profile.rs @@ -0,0 +1,41 @@ +//! Profile instrumentation helpers. +//! +//! Spans are always emitted by the phase modules (via the `tracing` crate, +//! which is zero-cost without a subscriber). Installing a subscriber that +//! renders those spans is gated on the `profile` feature so release builds +//! do not pull in `tracing-subscriber`. +//! +//! Plan O replaces the human-format subscriber below with JSON output plus +//! op-counter macros (`field_muls`, `merkle_hashes`, …) for ingestion by +//! Plan B's regression detector. + +/// Install a human-readable `tracing-subscriber` on stderr that mimics the +/// old `[PROFILE]` lines. Feature-gated: a no-op when `profile` is off. +/// +/// Safe to call multiple times; only the first call installs the global +/// subscriber. Returns `true` if this call performed the install. +#[cfg(feature = "profile")] +pub fn init() -> bool { + use tracing_subscriber::{fmt, prelude::*, EnvFilter}; + + // The default filter shows every span at INFO+; override via RUST_LOG. + let env_filter = + EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warp=info")); + + tracing_subscriber::registry() + .with(env_filter) + .with( + fmt::layer() + .with_target(false) + .with_span_events(fmt::format::FmtSpan::CLOSE) + .with_writer(std::io::stderr), + ) + .try_init() + .is_ok() +} + +/// No-op when the `profile` feature is off. +#[cfg(not(feature = "profile"))] +pub fn init() -> bool { + false +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index fc4cd34..55ed4d7 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,2 +1,4 @@ +pub mod oracle; +pub mod phases; pub mod query; pub mod transcript; diff --git a/src/protocol/oracle.rs b/src/protocol/oracle.rs new file mode 100644 index 0000000..9248e47 --- /dev/null +++ b/src/protocol/oracle.rs @@ -0,0 +1,78 @@ +//! Oracle abstraction for Warp's IOR phases. +//! +//! Paired spec: `docs/paper-mods/mod1_oracle.tex`. +//! +//! An [`Oracle`] is a single object that carries both views Warp's phases +//! need of a committed codeword: the raw evaluation table `f: [n] → F` +//! (BCS-native, index-queryable) and the implied multilinear extension +//! `\hat f: F^{log n} → F` (point-queryable). The multilinear extension is +//! materialised lazily on first point query and cached. +//! +//! The Merkle commitment of the codeword is **not** held here. In PESAT a +//! single Merkle tree covers many interleaved codewords +//! (`src/crypto/merkle/mod.rs::build_codeword_leaves`), so the tree is +//! tracked by the enclosing data structure (`PesatOutput`, +//! `AccumulatorWitness`) rather than 1:1 with the oracle. See the +//! Implementation note in `mod1_oracle.tex` §2. + +use ark_ff::Field; +use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; +use ark_std::log2; +use std::cell::OnceCell; + +/// A Warp oracle: a committed codeword together with its lazily-materialised +/// multilinear extension. +pub struct Oracle { + evals: Vec, + mle: OnceCell>, +} + +impl Oracle { + /// Wrap an existing evaluation table. + pub fn from_evals(evals: Vec) -> Self { + Self { + evals, + mle: OnceCell::new(), + } + } + + /// Borrow the evaluation table `f`. + pub fn evals(&self) -> &[F] { + &self.evals + } + + /// Consume the oracle and return the underlying evaluation table. + pub fn into_evals(self) -> Vec { + self.evals + } + + /// Length `n` of the evaluation table. + pub fn len(&self) -> usize { + self.evals.len() + } + + pub fn is_empty(&self) -> bool { + self.evals.is_empty() + } + + /// Index query: `f[i]`. + pub fn query_at_leaf(&self, idx: usize) -> F { + self.evals[idx] + } + + /// Point query on the multilinear extension: `\hat f(ζ)` for + /// `ζ ∈ F^{log n}`. Materialises the MLE on first call and caches it. + pub fn query_at_point(&self, point: &[F]) -> F { + let mle = self.mle.get_or_init(|| { + let log_n = log2(self.evals.len()) as usize; + DenseMultilinearExtension::from_evaluations_slice(log_n, &self.evals) + }); + mle.fix_variables(point)[0] + } +} + +impl From> for Oracle { + fn from(evals: Vec) -> Self { + Self::from_evals(evals) + } +} diff --git a/src/protocol/phases/batching.rs b/src/protocol/phases/batching.rs new file mode 100644 index 0000000..b977424 --- /dev/null +++ b/src/protocol/phases/batching.rs @@ -0,0 +1,112 @@ +//! Batching sumcheck phase. +//! +//! Paired spec: `docs/paper-mods/mod1_oracle.tex` (oracle composition). +//! Reduces the batched claim +//! +//! ```text +//! Σ_i ξ(i) · \hat f(ζ_i) = σ₂ +//! ``` +//! +//! to a single evaluation claim `μ = \hat f(α)` via the inner-product +//! sumcheck, with the CBBZ23 / HyperPlonk sparse-evaluation optimization +//! (`accumulate_sparse_evaluations`) folded in. +//! +//! Takes the already-sampled shift-query evaluation points as input so the +//! transcript order (queries sampled, then ξ, then sumcheck messages) is +//! preserved. See the orchestrator in `src/lib.rs`. + +use ark_ff::{Field, PrimeField}; +use ark_std::log2; +use efficient_sumcheck::{ + accumulate_sparse_evaluations, batched_constraint_poly, inner_product_sumcheck, +}; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; + +use crate::protocol::oracle::Oracle; +use crate::utils::poly::eq_poly; + +/// Output of the batching sumcheck: the reduced point `α` and the target +/// `μ = \hat f(α)`. +pub struct BatchingOutput { + pub alpha: Vec, + pub mu: F, +} + +/// Run the batching sumcheck. +/// +/// `zetas_prefix` must contain `1 + s + t` evaluation points in the order +/// `[ζ_0, ood_0, ..., ood_{s-1}, query_0, ..., query_{t-1}]`. +#[tracing::instrument( + name = "batching", + skip_all, + fields(s = s, t = t, log_n = log_n) +)] +pub fn prove( + prover_state: &mut ProverState, + oracle: &Oracle, + zetas_prefix: &[&[F]], + s: usize, + t: usize, + log_n: usize, +) -> BatchingOutput +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, +{ + let n = oracle.len(); + let r = 1 + s + t; + let log_r = log2(r) as usize; + debug_assert_eq!(zetas_prefix.len(), r); + + let xis = prover_state.verifier_messages_vec::(log_r); + + // compute evaluations for xi and the dense ood_evals_vec for the first 1+s zetas + let (xi_eq_evals, ood_evals_vec) = { + let _s = tracing::info_span!("batching.eq_evals").entered(); + let xi_eq_evals = (0..r).map(|i| eq_poly(&xis, i)).collect::>(); + let ood_evals_vec = (0..1 + s) + .map(|i| { + (0..n) + .map(|a| eq_poly(zetas_prefix[i], a) * xi_eq_evals[i]) + .collect::>() + }) + .collect::>(); + (xi_eq_evals, ood_evals_vec) + }; + + // [CBBZ23] / HyperPlonk optimization for the t sparse shift-query zetas. + let id_non_0_eval_sums = { + let _s = tracing::info_span!("batching.accumulate_sparse").entered(); + accumulate_sparse_evaluations(zetas_prefix.to_vec(), xi_eq_evals, s, r) + }; + + // call efficient sumcheck for batched_constraint checks + let alpha = { + let _s = tracing::info_span!("batching.sumcheck").entered(); + inner_product_sumcheck( + &mut oracle.evals().to_vec(), + &mut batched_constraint_poly(&ood_evals_vec, &id_non_0_eval_sums), + prover_state, + ) + .verifier_messages + }; + + let mu = oracle.query_at_point(&alpha); + + BatchingOutput { alpha, mu } +} + +/// Reduce the batching (inner-product / multilinear) sumcheck's per-round +/// messages against the verifier challenges `α`. Each round message is +/// `[a, b]` where `h(X) = a·(1-2X) + b·X + (prev_target - b)·X²`; the +/// caller's `a, b` unpacking mirrors `src/protocol/transcript/verifier.rs`. +#[tracing::instrument(name = "batching.verify", skip_all)] +pub(crate) fn verify_claim(sigma_2: F, sums_per_round: Vec<[F; 2]>, alpha: &[F]) -> F +where + F: Field, +{ + let mut target = sigma_2; + for ([a, b], x) in sums_per_round.into_iter().zip(alpha) { + target = (target - b) * x.square() + a * (F::one() - x.double()) + b * x; + } + target +} diff --git a/src/protocol/phases/mod.rs b/src/protocol/phases/mod.rs new file mode 100644 index 0000000..9eb1c4c --- /dev/null +++ b/src/protocol/phases/mod.rs @@ -0,0 +1,23 @@ +//! Warp IOR phases as first-class modules. +//! +//! Paired spec: `docs/paper-mods/mod1_oracle.tex` (shared Oracle composition) +//! and the forthcoming `docs/paper-mods/mod2_structured_sumcheck.tex`, +//! `mod3_accumulator_state.tex`. +//! +//! Each submodule implements one IOR from the Warp construction. The phase +//! functions are concrete (no `IOR` trait) but share a consistent shape: +//! +//! - **prove**: takes the current prover state, the accumulator, and any +//! fresh inputs; runs the IOR's prover; returns the reduced claim and any +//! emitted [`Oracle`](super::oracle::Oracle)s. +//! - **verify** (where applicable): consumes a subset of +//! `DerivedRandomness` plus the prior claim; returns the reduced claim. +//! +//! The top-level orchestrators live in `src/lib.rs::WARP::prove` and +//! `::verify`, which thread state between phases. + +pub mod batching; +pub mod ood; +pub mod pesat; +pub mod proximity; +pub mod twin_constraint; diff --git a/src/protocol/phases/ood.rs b/src/protocol/phases/ood.rs new file mode 100644 index 0000000..4e29945 --- /dev/null +++ b/src/protocol/phases/ood.rs @@ -0,0 +1,44 @@ +//! Out-of-domain sampling phase. +//! +//! Paired spec: `docs/paper-mods/mod1_oracle.tex`. This phase is a thin +//! composition of point queries on the committed oracle — see +//! [`Oracle::query_at_point`](crate::protocol::oracle::Oracle::query_at_point). +//! The verifier derives the same random points from the transcript. + +use ark_ff::{Field, PrimeField}; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; + +use crate::protocol::oracle::Oracle; + +/// Output of the OOD phase: the flat challenge vector and the prover's +/// answers at each chunked evaluation point. +pub struct OodOutput { + /// Flat challenge vector of length `s · log_n`. + pub samples_flat: Vec, + /// Answers `\hat f(ζ_j)` for each of the `s` chunked challenges. + pub answers: Vec, +} + +/// Run the OOD phase: sample `s` evaluation points, query the oracle at +/// each, absorb the answers. +#[tracing::instrument(name = "ood", skip_all, fields(s = s, log_n = log_n))] +pub fn prove( + prover_state: &mut ProverState, + oracle: &Oracle, + s: usize, + log_n: usize, +) -> OodOutput +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, +{ + let samples_flat = prover_state.verifier_messages_vec::(s * log_n); + let answers = samples_flat + .chunks(log_n) + .map(|zeta| oracle.query_at_point(zeta)) + .collect::>(); + prover_state.prover_messages(&answers); + OodOutput { + samples_flat, + answers, + } +} diff --git a/src/protocol/phases/pesat.rs b/src/protocol/phases/pesat.rs new file mode 100644 index 0000000..766c93f --- /dev/null +++ b/src/protocol/phases/pesat.rs @@ -0,0 +1,89 @@ +//! PESAT Reduction phase. +//! +//! Paired spec: `docs/paper-mods/mod1_oracle.tex` (oracle composition). +//! Implements Phase 2 of the Warp prover: encode fresh witnesses into +//! codewords, commit via an interleaved Merkle tree, absorb commitment and +//! code evaluations, and derive the τ zero-check challenges. +//! +//! Under Modification 1's oracle framing, this IOR has signature +//! `(statement, witnesses) -> (PesatOutput{codewords, commit, claims, τs})`; +//! downstream phases consume these as oracles. +//! +//! No verifier-side work lives here — PESAT emits oracles and τs that the +//! verifier derives afresh from the transcript via +//! `src/protocol/transcript/verifier.rs::derive_randomness`. + +use ark_codes::traits::LinearCode; +use ark_crypto_primitives::{ + crh::{CRHScheme, TwoToOneCRHScheme}, + merkle_tree::{Config, MerkleTree}, +}; +use ark_ff::{Field, PrimeField}; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; + +use crate::crypto::merkle::build_codeword_leaves; +use crate::error::ProverError; +use crate::types::PesatOutput; + +/// Run the PESAT Reduction prover. +#[tracing::instrument( + name = "pesat", + skip_all, + fields(l1 = l1, log_m = log_m, n_witnesses = witnesses.len()) +)] +pub(crate) fn prove( + prover_state: &mut ProverState, + code: &C, + mt_leaf_hash_params: &::Parameters, + mt_two_to_one_hash_params: &::Parameters, + witnesses: &[Vec], + l1: usize, + log_m: usize, +) -> Result, ProverError> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + C: LinearCode, + MT: Config + From<[u8; 32]>>, +{ + // a. encode witnesses + let (codewords, leaves) = { + let _s = tracing::info_span!("pesat.encode").entered(); + build_codeword_leaves(code, witnesses, l1) + }; + + // b. evaluation claims + let mus = codewords.iter().map(|f| f[0]).collect::>(); + + // c. commit to witnesses + let td_0 = { + let _s = tracing::info_span!("pesat.merkle_commit").entered(); + MerkleTree::::new( + mt_leaf_hash_params, + mt_two_to_one_hash_params, + leaves.chunks_exact(l1).collect::>(), + )? + }; + + // d. absorb commitment and code evaluations; e/f. derive τ challenges. + let taus = { + let _s = tracing::info_span!("pesat.absorb_and_derive").entered(); + let root_bytes: [u8; 32] = td_0 + .root() + .as_ref() + .try_into() + .expect("root must be 32 bytes"); + prover_state.prover_message(&root_bytes); + prover_state.prover_messages(&mus); + + (0..l1) + .map(|_| prover_state.verifier_messages_vec::(log_m)) + .collect::>() + }; + + Ok(PesatOutput { + codewords, + td_0, + mus, + taus, + }) +} diff --git a/src/protocol/phases/proximity.rs b/src/protocol/phases/proximity.rs new file mode 100644 index 0000000..e1e2876 --- /dev/null +++ b/src/protocol/phases/proximity.rs @@ -0,0 +1,148 @@ +//! Proximity / shift-query phase. +//! +//! Paired spec: `docs/paper-mods/mod1_oracle.tex` — index queries on the +//! committed oracles. Generates the authentication paths for each shift +//! query leaf against both the fresh PESAT commitment and each accumulated +//! commitment, and collects the codeword values at those leaves. +//! +//! The query indices themselves are sampled from the transcript **before** +//! this phase — see the orchestrator in `src/lib.rs` — so the batching +//! sumcheck can consume the same indices. + +use ark_crypto_primitives::{ + crh::{CRHScheme, TwoToOneCRHScheme}, + merkle_tree::{Config, MerkleTree, Path}, + Error, +}; +use ark_ff::Field; + +use crate::crypto::merkle::compute_auth_paths; +use crate::error::VerifierError; +use crate::protocol::query::QueryIndices; +use crate::BoolResult; + +pub struct ProximityOutput { + pub auth_0: Vec>, + pub auth_j: Vec>>, + pub shift_query_answers: Vec>, +} + +/// Open the proximity queries: generate auth paths for the fresh commitment +/// and each accumulated commitment, and collect the codeword values at every +/// queried leaf. +/// +/// `all_codewords` must list the **accumulated** codewords first, then the +/// **fresh** PESAT codewords, matching the verifier's expectation at +/// `lib.rs::verify` (the `[l2..]` slice is the fresh chunk). +#[tracing::instrument( + name = "proximity", + skip_all, + fields( + n_queries = queries.leaf_positions.len(), + n_accumulators = acc_td.len(), + n_codewords = all_codewords.len(), + ) +)] +pub fn prove( + queries: &QueryIndices, + td_0: &MerkleTree, + acc_td: &[MerkleTree], + all_codewords: &[Vec], +) -> Result, Error> +where + F: Field, + MT: Config, +{ + let auth_0 = { + let _s = tracing::info_span!("proximity.auth_0").entered(); + compute_auth_paths(td_0, &queries.leaf_positions)? + }; + + let auth_j = { + let _s = tracing::info_span!("proximity.auth_j").entered(); + acc_td + .iter() + .map(|td| compute_auth_paths(td, &queries.leaf_positions)) + .collect::>>, Error>>()? + }; + + let shift_query_answers = { + let _s = tracing::info_span!("proximity.shift_queries").entered(); + let mut answers = + vec![vec![F::default(); all_codewords.len()]; queries.leaf_positions.len()]; + for (i, idx) in queries.leaf_positions.iter().enumerate() { + let row = all_codewords.iter().map(|f| f[*idx]).collect::>(); + answers[i] = row; + } + answers + }; + + Ok(ProximityOutput { + auth_0, + auth_j, + shift_query_answers, + }) +} + +/// Verify the proximity (shift-query) openings. +/// +/// - `auth_0` opens the fresh PESAT tree at each query; expected leaves are +/// the `l2..` slice of each `shift_query_answers` row (the fresh chunk). +/// - `auth_j` opens each of `l2` accumulated trees at each query; expected +/// leaf for accumulator `i` at query `j` is `shift_query_answers[j][i]`. +#[allow(clippy::too_many_arguments)] +#[tracing::instrument( + name = "proximity.verify", + skip_all, + fields(t = t, l2 = l2) +)] +pub(crate) fn verify( + queries: &QueryIndices, + rt_0: &MT::InnerDigest, + l2_roots: &[MT::InnerDigest], + auth_0: &[Path], + auth_j: &[Vec>], + shift_query_answers: &[Vec], + mt_leaf_hash_params: &::Parameters, + mt_two_to_one_hash_params: &::Parameters, + l2: usize, + t: usize, +) -> Result<(), VerifierError> +where + F: Field, + MT: Config, +{ + (shift_query_answers.len() == t).ok_or_err(VerifierError::NumShiftQueries)?; + + for (i, path) in auth_0.iter().enumerate() { + (path.leaf_index == queries.leaf_positions[i]) + .ok_or_err(VerifierError::ShiftQueryIndex)?; + + let is_valid = path.verify( + mt_leaf_hash_params, + mt_two_to_one_hash_params, + rt_0, + &shift_query_answers[i][l2..], // leaves are evaluations of the l1 codewords + )?; + is_valid.ok_or_err(VerifierError::ShiftQuery)?; + } + + (auth_j.len() == l2).ok_or_err(VerifierError::NumL2Instances)?; + for (i, paths) in auth_j.iter().enumerate() { + (paths.len() == t).ok_or_err(VerifierError::NumShiftQueries)?; + let root = &l2_roots[i]; + for (j, path) in paths.iter().enumerate() { + (path.leaf_index == queries.leaf_positions[j]) + .ok_or_err(VerifierError::ShiftQueryIndex)?; + let is_valid = path.verify( + mt_leaf_hash_params, + mt_two_to_one_hash_params, + root, + [shift_query_answers[j][i]], + )?; + is_valid.ok_or_err(VerifierError::ShiftQuery)?; + } + } + + Ok(()) +} diff --git a/src/protocol/phases/twin_constraint.rs b/src/protocol/phases/twin_constraint.rs new file mode 100644 index 0000000..779ee8f --- /dev/null +++ b/src/protocol/phases/twin_constraint.rs @@ -0,0 +1,242 @@ +//! Twin-constraint sumcheck phase. +//! +//! Paired spec: `docs/paper-mods/mod1_oracle.tex` (oracle composition). +//! The forthcoming `docs/paper-mods/mod2_structured_sumcheck.tex` will +//! promote this phase's fused-fold prover to a first-class paper primitive. +//! +//! Reduces the claim +//! +//! ```text +//! Σ_i τ(i) · (f(i) + ω · p(i)) = σ₁ +//! ``` +//! +//! to evaluations at a random point γ via protogalaxy folding, where +//! - `f(X) = fold(α, oracle_evals)` — folded codeword check +//! - `p(X) = fold(β, Az·Bz − Cz)` — folded R1CS constraint check +//! - `t(X)` = linear interpolation of τ — equality polynomial +//! +//! Each round's round polynomial has the form `h(X) = (f(X) + ω·p(X))·t(X)`. + +use ark_ff::{Field, PrimeField}; +use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial}; +use efficient_sumcheck::{ + coefficient_sumcheck::{coefficient_sumcheck, RoundPolyEvaluator}, + folding::protogalaxy, + hypercube::Hypercube, + order_strategy::AscendingOrder, +}; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; + +use crate::protocol::oracle::Oracle; +use crate::relations::r1cs::R1CSConstraints; +use crate::types::AccumulatorInstance; +use crate::utils::{concat_slices, poly::eq_poly}; +use ark_crypto_primitives::merkle_tree::Config; + +/// Degree-1 polynomial interpolating two field elements: `lo + (hi - lo)·X`. +fn linear_poly(lo: F, hi: F) -> DensePolynomial { + DensePolynomial::from_coefficients_vec(vec![lo, hi - lo]) +} + +/// A single R1CS constraint row: sparse representations of A, B, and C. +type R1CSConstraint = (Vec<(F, usize)>, Vec<(F, usize)>, Vec<(F, usize)>); + +/// Evaluate one R1CS constraint `Az·Bz - Cz` as a degree-2 polynomial +/// from two witness vectors `z0`, `z1`. +fn eval_r1cs_constraint_poly( + (a, b, c): &R1CSConstraint, + z0: &[F], + z1: &[F], +) -> DensePolynomial { + let eval = |lc: &[(F, usize)], z: &[F]| lc.iter().map(|(t, i)| z[*i] * t).sum::(); + let (a0, b0, c0) = (eval(a, z0), eval(b, z0), eval(c, z0)); + let (a1, b1, c1) = (eval(a, z1) - a0, eval(b, z1) - b0, eval(c, z1) - c0); + DensePolynomial::from_coefficients_vec(vec![a0 * b0 - c0, a0 * b1 + a1 * b0 - c1, a1 * b1]) +} + +/// Round-polynomial evaluator fusing α-fold, β-fold, and τ-linear into a +/// single sumcheck pass. See `mod2_structured_sumcheck.tex` (stub) — the +/// fusion will be promoted to a paper-level primitive in Plan B'. +struct TwinConstraintEvaluator<'a, F: Field> { + r1cs: &'a R1CSConstraints, + omega: F, + degree: usize, +} + +impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { + fn degree(&self) -> usize { + self.degree + } + + fn accumulate_pair(&self, coeffs: &mut [F], tw: &[(&[F], &[F])], pw: &[(F, F)]) { + // tw[0] = (u_even, u_odd), tw[1] = (z_even, z_odd), + // tw[2] = (a_even, a_odd), tw[3] = (b_even, b_odd) + // pw[0] = (tau_even, tau_odd) + let (u_even, u_odd) = tw[0]; + let (z_even, z_odd) = tw[1]; + let (a_even, a_odd) = tw[2]; + let (b_even, b_odd) = tw[3]; + let (tau_even, tau_odd) = pw[0]; + + // f(X) = fold(α, oracle_evals): protogalaxy fold over α pairs and linear polys from u + let f = protogalaxy::fold( + a_even.iter().zip(a_odd).map(|(&l, &r)| (l, r - l)), + u_even + .iter() + .zip(u_odd) + .map(|(&l, &r)| linear_poly(l, r)) + .collect(), + ); + + // p(X) = fold(β, Az·Bz - Cz): protogalaxy fold over β pairs and R1CS constraint polys + let p = protogalaxy::fold( + b_even.iter().zip(b_odd).map(|(&l, &r)| (l, r - l)), + self.r1cs + .iter() + .map(|c| eval_r1cs_constraint_poly(c, z_even, z_odd)) + .collect(), + ); + + // t(X) = tau_even + (tau_odd - tau_even) · X + let t = linear_poly(tau_even, tau_odd); + + // h(X) = (f(X) + ω·p(X)) · t(X) + let h = (f + p * self.omega).naive_mul(&t); + + for (c, &hc) in coeffs.iter_mut().zip(h.coeffs.iter()) { + *c += hc; + } + } +} + +/// Output of the twin-constraint sumcheck. All oracles / vectors are the +/// reduced claim state consumed by downstream phases (OOD, batching, final +/// target). +pub struct TwinConstraintOutput { + /// Reduced codeword oracle `f` — consumed by OOD, batching, proximity. + pub f: Oracle, + /// Reduced witness vector `z = (x, w)` — consumed by η evaluation. + pub z: Vec, + /// New code evaluation point `ζ₀` — becomes the new accumulator α. + pub zeta_0: Vec, + /// Reduced τ — becomes the τ component of the new accumulator β. + pub beta_tau: Vec, +} + +/// Run the twin-constraint sumcheck prover. +/// +/// Takes codeword slices by reference so the caller (orchestrator) can hand +/// them to [`proximity::prove`](super::proximity::prove) after this phase +/// returns. `fresh_taus` and `acc_instance` are consumed into the sumcheck +/// tables — neither is needed downstream. +#[allow(clippy::too_many_arguments)] +#[tracing::instrument( + name = "twin_constraint", + skip_all, + fields(log_l = log_l, log_m = log_m, log_n = log_n) +)] +pub fn prove( + prover_state: &mut ProverState, + fresh_codewords: &[Vec], + fresh_taus: Vec>, + acc_instance: AccumulatorInstance, + acc_witness_f: &[Vec], + acc_witness_w: &[Vec], + instances: &[Vec], + witnesses: &[Vec], + r1cs: &R1CSConstraints, + log_l: usize, + log_m: usize, + log_n: usize, +) -> TwinConstraintOutput +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + MT: Config + From<[u8; 32]>>, +{ + let l1 = fresh_codewords.len(); + + // a. zero-check randomness + let omega: F = prover_state.verifier_message(); + let tau = prover_state.verifier_messages_vec::(log_l); + + // b. assemble sumcheck tables + let tau_eq_evals = Hypercube::::new(log_l) + .map(|(index, _point)| eq_poly(&tau, index)) + .collect::>(); + + let alpha_vecs = concat_slices(&acc_instance.alpha, &vec![vec![F::zero(); log_n]; l1]); + + let z_vecs: Vec> = acc_instance + .beta + .1 + .iter() + .zip(acc_witness_w) + .chain(instances.iter().zip(witnesses)) + .map(|(x, w)| concat_slices(x, w)) + .collect(); + + let beta_vecs: Vec> = acc_instance.beta.0.into_iter().chain(fresh_taus).collect(); + + let mut tablewise = [ + concat_slices(acc_witness_f, fresh_codewords), // u + z_vecs, // z + alpha_vecs, // a + beta_vecs, // b + ]; + let mut pw = [tau_eq_evals]; // tau + + let degree = 1 + (log_n + 1).max(log_m + 2); + let evaluator = TwinConstraintEvaluator { + r1cs, + omega, + degree, + }; + + // c. run the sumcheck + let sc = { + let _s = tracing::info_span!("twin_constraint.sumcheck").entered(); + coefficient_sumcheck(&evaluator, &mut tablewise, &mut pw, log_l, prover_state) + }; + debug_assert_eq!(sc.verifier_messages.len(), log_l); + + // d. pop reduced tables — each group has one table left after log_l rounds + let [mut u_red, mut z_red, mut a_red, mut b_red] = tablewise; + let f = u_red.pop().unwrap(); + let z = z_red.pop().unwrap(); + let zeta_0 = a_red.pop().unwrap(); + let beta_tau = b_red.pop().unwrap(); + + TwinConstraintOutput { + f: Oracle::from_evals(f), + z, + zeta_0, + beta_tau, + } +} + +/// Reduce the twin-constraint sumcheck's per-round coefficient messages +/// against the verifier challenges `γ`. Returns the final reduced target. +/// +/// The prover sends only `d` coefficients per round; the leading coefficient +/// `c_d` is derived from the round claim `T = 2·c_0 + c_1 + … + c_d` so +/// `c_d = T − 2·c_0 − c_1 − … − c_{d−1}`. Matches the encoding in +/// `src/protocol/transcript/verifier.rs::derive_randomness`. +#[tracing::instrument(name = "twin_constraint.verify", skip_all)] +pub(crate) fn verify_claim( + sigma_1: F, + coeffs_per_round: Vec>, + gamma: &[F], +) -> F +where + F: Field, +{ + let mut target = sigma_1; + for (mut coeffs, g) in coeffs_per_round.into_iter().zip(gamma) { + let partial_sum: F = coeffs.iter().skip(1).copied().sum(); + let leading = target - coeffs[0].double() - partial_sum; + coeffs.push(leading); + let h = DensePolynomial::from_coefficients_vec(coeffs); + target = h.evaluate(g); + } + target +} From 2a5379ba5d504df5fbc61e9fe88175f9b02e56fe Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Sat, 18 Apr 2026 13:50:05 +0200 Subject: [PATCH 10/68] =?UTF-8?q?Plan=20O:=20structured=20observability=20?= =?UTF-8?q?=E2=80=94=20op=20counters,=20cpu=5Fns,=20JSON=20layer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructures src/profile.rs into a module directory with four parts, all gated behind the existing `profile` cargo feature: - counters: thread-local Cell counters with a `count_ops!` macro. Snapshots + deltas let a subscriber diff across a span's lifetime. Tracks coarse call-site events (MerkleTreeBuilds, MerklePathsGenerated, MleMaterializations, OracleLeafQueries, OraclePointQueries, TwinConstraintRounds, BatchingRounds, OodPointQueries, EncodeCalls, MerklePathsVerified). Field-level op counts are out of scope — they need an F newtype or an arkworks fork; deferred. - timing: `thread_cpu_ns()` via clock_gettime(CLOCK_THREAD_CPUTIME_ID) on Linux and macOS. Distinguishes blocked-on-IO from blocked-on-compute in a way wall-time cannot. - rss: `peak_rss_bytes()` via getrusage(RUSAGE_SELF), normalising the Linux-kB vs macOS-bytes discrepancy. - layer: a tracing `Layer` that captures a span's counters + timing + rss on enter, differences them on close, and emits one newline- delimited JSON record per span. Schema tag `warp.profile.v1`; per-record fields are {phase, wall_ns, cpu_ns, rss_delta_bytes, counters, dimensions}. Dimensions come from span numeric fields (log_l, log_m, log_n, etc.), captured via a tracing Visit. Phase modules and the Oracle are instrumented at call sites so the JSON records carry meaningful counter deltas. Without the feature every count_ops! call and every timing/rss wrapper compiles to a no-op. tests/profile_json.rs is a feature-gated integration test that installs the JSON layer against an in-memory sink, runs a full hashchain prove, and asserts every phase emits a well-formed record. It's the reference shape Plan B's regression detector will consume. Verification matrix (all green): - cargo test (no features) : 14/14 - cargo test --features profile : 14/14 + profile_json 1/1 - cargo clippy --all-targets : clean - cargo clippy --all-targets --all-features : clean --- Cargo.toml | 12 +- src/lib.rs | 1 + src/profile.rs | 41 ----- src/profile/counters.rs | 237 +++++++++++++++++++++++++ src/profile/layer.rs | 189 ++++++++++++++++++++ src/profile/mod.rs | 84 +++++++++ src/profile/rss.rs | 42 +++++ src/profile/timing.rs | 38 ++++ src/protocol/oracle.rs | 5 + src/protocol/phases/batching.rs | 3 + src/protocol/phases/ood.rs | 2 + src/protocol/phases/pesat.rs | 3 + src/protocol/phases/proximity.rs | 8 + src/protocol/phases/twin_constraint.rs | 2 + tests/profile_json.rs | 169 ++++++++++++++++++ 15 files changed, 790 insertions(+), 46 deletions(-) delete mode 100644 src/profile.rs create mode 100644 src/profile/counters.rs create mode 100644 src/profile/layer.rs create mode 100644 src/profile/mod.rs create mode 100644 src/profile/rss.rs create mode 100644 src/profile/timing.rs create mode 100644 tests/profile_json.rs diff --git a/Cargo.toml b/Cargo.toml index 4f9e0f3..d388d68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,8 @@ spongefish = { git = "https://github.com/z-tech/spongefish.git", branch = "small efficient-sumcheck = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git", branch = "z-tech/simd_goldilocks_experimental" } thiserror = "2.0.16" tracing = { version = "0.1", default-features = false, features = ["std", "attributes"] } -tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter"], optional = true } +tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter", "json", "registry"], optional = true } +libc = { version = "0.2", optional = true } ark-codes = { git = "https://github.com/dmpierre/ark-codes.git" } @@ -53,10 +54,11 @@ default = ["asm"] asm = ["ark-ff/asm"] -# Turns on a `tracing-subscriber` that emits phase timing and dimension fields -# to stderr in the same spirit as the old `[PROFILE]` eprintln! lines. -# Plan O replaces this with structured JSON + op counters. -profile = ["dep:tracing-subscriber"] +# Opt-in profiling: brings in tracing-subscriber + libc so we can emit +# structured per-phase records (wall_ns, cpu_ns, peak_rss_delta, op counters). +# Release builds with `profile` off pull in neither tracing-subscriber nor +# libc as direct deps of `warp`. +profile = ["dep:tracing-subscriber", "dep:libc"] [[bench]] name = "warp_rs" diff --git a/src/lib.rs b/src/lib.rs index 82e08a1..827273a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -197,6 +197,7 @@ impl< let td = { let _s = tracing::info_span!("warp.commit_new_oracle").entered(); + count_ops!(MerkleTreeBuilds); MerkleTree::::new( &self.params.mt_leaf_hash_params, &self.params.mt_two_to_one_hash_params, diff --git a/src/profile.rs b/src/profile.rs deleted file mode 100644 index 51a3b17..0000000 --- a/src/profile.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! Profile instrumentation helpers. -//! -//! Spans are always emitted by the phase modules (via the `tracing` crate, -//! which is zero-cost without a subscriber). Installing a subscriber that -//! renders those spans is gated on the `profile` feature so release builds -//! do not pull in `tracing-subscriber`. -//! -//! Plan O replaces the human-format subscriber below with JSON output plus -//! op-counter macros (`field_muls`, `merkle_hashes`, …) for ingestion by -//! Plan B's regression detector. - -/// Install a human-readable `tracing-subscriber` on stderr that mimics the -/// old `[PROFILE]` lines. Feature-gated: a no-op when `profile` is off. -/// -/// Safe to call multiple times; only the first call installs the global -/// subscriber. Returns `true` if this call performed the install. -#[cfg(feature = "profile")] -pub fn init() -> bool { - use tracing_subscriber::{fmt, prelude::*, EnvFilter}; - - // The default filter shows every span at INFO+; override via RUST_LOG. - let env_filter = - EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warp=info")); - - tracing_subscriber::registry() - .with(env_filter) - .with( - fmt::layer() - .with_target(false) - .with_span_events(fmt::format::FmtSpan::CLOSE) - .with_writer(std::io::stderr), - ) - .try_init() - .is_ok() -} - -/// No-op when the `profile` feature is off. -#[cfg(not(feature = "profile"))] -pub fn init() -> bool { - false -} diff --git a/src/profile/counters.rs b/src/profile/counters.rs new file mode 100644 index 0000000..3aa6077 --- /dev/null +++ b/src/profile/counters.rs @@ -0,0 +1,237 @@ +//! Thread-local op counters. +//! +//! Call-site counters for operations we can cheaply observe at the +//! boundaries of our crate (Merkle tree builds, path generations, MLE +//! materialisations, sumcheck rounds). Field-level ops (muls/adds) are +//! **not** counted here — they would require newtyping `F` or forking +//! arkworks. That's deferred; Plan O's goal is asymptotic validation, not +//! per-op accounting. +//! +//! All counters compile to no-ops without the `profile` feature: the +//! [`count_ops!`] macro expands to `()`. + +#[cfg(feature = "profile")] +use std::cell::Cell; + +/// Counter slots. Adding a new one: extend the enum, extend [`Counters`], +/// extend the [`count_ops!`] match, extend [`snapshot`] and [`delta`]. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum Counter { + EncodeCalls, + MerkleTreeBuilds, + MerklePathsGenerated, + MerklePathsVerified, + MleMaterializations, + OracleLeafQueries, + OraclePointQueries, + TwinConstraintRounds, + BatchingRounds, + OodPointQueries, +} + +impl Counter { + pub const ALL: &'static [Counter] = &[ + Counter::EncodeCalls, + Counter::MerkleTreeBuilds, + Counter::MerklePathsGenerated, + Counter::MerklePathsVerified, + Counter::MleMaterializations, + Counter::OracleLeafQueries, + Counter::OraclePointQueries, + Counter::TwinConstraintRounds, + Counter::BatchingRounds, + Counter::OodPointQueries, + ]; + + pub fn name(self) -> &'static str { + match self { + Counter::EncodeCalls => "encode_calls", + Counter::MerkleTreeBuilds => "merkle_tree_builds", + Counter::MerklePathsGenerated => "merkle_paths_generated", + Counter::MerklePathsVerified => "merkle_paths_verified", + Counter::MleMaterializations => "mle_materializations", + Counter::OracleLeafQueries => "oracle_leaf_queries", + Counter::OraclePointQueries => "oracle_point_queries", + Counter::TwinConstraintRounds => "twin_constraint_rounds", + Counter::BatchingRounds => "batching_rounds", + Counter::OodPointQueries => "ood_point_queries", + } + } +} + +#[cfg(feature = "profile")] +thread_local! { + static COUNTERS: Counters = const { Counters::new() }; +} + +#[cfg(feature = "profile")] +struct Counters { + encode_calls: Cell, + merkle_tree_builds: Cell, + merkle_paths_generated: Cell, + merkle_paths_verified: Cell, + mle_materializations: Cell, + oracle_leaf_queries: Cell, + oracle_point_queries: Cell, + twin_constraint_rounds: Cell, + batching_rounds: Cell, + ood_point_queries: Cell, +} + +#[cfg(feature = "profile")] +impl Counters { + const fn new() -> Self { + Self { + encode_calls: Cell::new(0), + merkle_tree_builds: Cell::new(0), + merkle_paths_generated: Cell::new(0), + merkle_paths_verified: Cell::new(0), + mle_materializations: Cell::new(0), + oracle_leaf_queries: Cell::new(0), + oracle_point_queries: Cell::new(0), + twin_constraint_rounds: Cell::new(0), + batching_rounds: Cell::new(0), + ood_point_queries: Cell::new(0), + } + } + + fn cell(&self, c: Counter) -> &Cell { + match c { + Counter::EncodeCalls => &self.encode_calls, + Counter::MerkleTreeBuilds => &self.merkle_tree_builds, + Counter::MerklePathsGenerated => &self.merkle_paths_generated, + Counter::MerklePathsVerified => &self.merkle_paths_verified, + Counter::MleMaterializations => &self.mle_materializations, + Counter::OracleLeafQueries => &self.oracle_leaf_queries, + Counter::OraclePointQueries => &self.oracle_point_queries, + Counter::TwinConstraintRounds => &self.twin_constraint_rounds, + Counter::BatchingRounds => &self.batching_rounds, + Counter::OodPointQueries => &self.ood_point_queries, + } + } +} + +/// Bump a counter on the current thread. +#[cfg(feature = "profile")] +#[inline] +pub fn bump(c: Counter, n: u64) { + COUNTERS.with(|cs| { + let cell = cs.cell(c); + cell.set(cell.get().saturating_add(n)); + }); +} + +#[cfg(not(feature = "profile"))] +#[inline] +pub fn bump(_c: Counter, _n: u64) {} + +/// Snapshot every counter on the current thread. +#[cfg(feature = "profile")] +pub fn snapshot() -> Snapshot { + let mut values = [0u64; Counter::ALL.len()]; + COUNTERS.with(|cs| { + for (i, &c) in Counter::ALL.iter().enumerate() { + values[i] = cs.cell(c).get(); + } + }); + Snapshot { values } +} + +#[cfg(not(feature = "profile"))] +pub fn snapshot() -> Snapshot { + Snapshot {} +} + +/// A point-in-time reading of all counters. Subtract two snapshots with +/// [`Snapshot::delta`] to get the change over an interval. +#[cfg(feature = "profile")] +#[derive(Clone, Copy, Debug)] +pub struct Snapshot { + values: [u64; Counter::ALL.len()], +} + +#[cfg(feature = "profile")] +impl Snapshot { + pub fn get(&self, c: Counter) -> u64 { + let idx = Counter::ALL.iter().position(|x| *x == c).unwrap(); + self.values[idx] + } + + /// `later.delta(&earlier)` returns `later - earlier`, per counter. + pub fn delta(&self, earlier: &Snapshot) -> Delta { + let mut values = [0u64; Counter::ALL.len()]; + for (i, slot) in values.iter_mut().enumerate() { + *slot = self.values[i].saturating_sub(earlier.values[i]); + } + Delta { values } + } +} + +#[cfg(not(feature = "profile"))] +#[derive(Clone, Copy, Debug)] +pub struct Snapshot {} + +#[cfg(not(feature = "profile"))] +impl Snapshot { + pub fn get(&self, _c: Counter) -> u64 { + 0 + } + pub fn delta(&self, _earlier: &Snapshot) -> Delta { + Delta {} + } +} + +#[cfg(feature = "profile")] +#[derive(Clone, Copy, Debug)] +pub struct Delta { + values: [u64; Counter::ALL.len()], +} + +#[cfg(feature = "profile")] +impl Delta { + pub fn get(&self, c: Counter) -> u64 { + let idx = Counter::ALL.iter().position(|x| *x == c).unwrap(); + self.values[idx] + } + + /// Iterate over `(Counter, delta)` pairs whose delta is non-zero. + pub fn iter_nonzero(&self) -> impl Iterator + '_ { + Counter::ALL + .iter() + .zip(self.values.iter()) + .filter_map(|(c, v)| if *v > 0 { Some((*c, *v)) } else { None }) + } +} + +#[cfg(not(feature = "profile"))] +#[derive(Clone, Copy, Debug)] +pub struct Delta {} + +#[cfg(not(feature = "profile"))] +impl Delta { + pub fn get(&self, _c: Counter) -> u64 { + 0 + } + pub fn iter_nonzero(&self) -> std::iter::Empty<(Counter, u64)> { + std::iter::empty() + } +} + +/// Increment a named counter by 1 (or by a caller-supplied amount). +/// +/// Under `profile` feature: compiles to a thread-local Cell bump. +/// Without `profile`: compiles to `()`. +/// +/// ```ignore +/// count_ops!(MerkleTreeBuilds); // +1 +/// count_ops!(OraclePointQueries, 3); // +3 +/// ``` +#[macro_export] +macro_rules! count_ops { + ($counter:ident) => { + $crate::profile::counters::bump($crate::profile::counters::Counter::$counter, 1); + }; + ($counter:ident, $n:expr) => { + $crate::profile::counters::bump($crate::profile::counters::Counter::$counter, $n); + }; +} diff --git a/src/profile/layer.rs b/src/profile/layer.rs new file mode 100644 index 0000000..3dc7baa --- /dev/null +++ b/src/profile/layer.rs @@ -0,0 +1,189 @@ +//! Tracing Layer that emits one JSON record per closed span. +//! +//! Span fields are captured via a [`FieldVisitor`] on `on_new_span`; +//! counter / timing snapshots are stashed in span extensions on +//! `on_enter` and differenced on `on_close`. +//! +//! Schema (version `warp.profile.v1`): +//! +//! ```json +//! { +//! "schema": "warp.profile.v1", +//! "phase": "twin_constraint", +//! "wall_ns": 123456, +//! "cpu_ns": 98765, +//! "rss_delta_bytes": 1024, +//! "counters": { "twin_constraint_rounds": 10, ... }, +//! "dimensions": { "log_l": 3, "log_m": 2, "log_n": 5 } +//! } +//! ``` +//! +//! One record per line (newline-delimited JSON), written to the configured +//! `io::Write` sink. The enclosing module gates this file behind the +//! `profile` feature; no per-file `cfg` is needed here. + +use std::collections::BTreeMap; +use std::io::Write; +use std::sync::Mutex; +use std::time::Instant; + +use tracing::field::{Field, Visit}; +use tracing::span::{Attributes, Id}; +use tracing::Subscriber; +use tracing_subscriber::layer::Context; +use tracing_subscriber::registry::LookupSpan; +use tracing_subscriber::Layer; + +use crate::profile::counters::{self, Counter, Snapshot}; +use crate::profile::{rss, timing}; + +/// What we stash on each span at enter-time. +struct SpanStart { + wall: Instant, + cpu_ns: Option, + rss_bytes: Option, + counters: Snapshot, +} + +/// Dimensions (numeric span fields) captured at span-creation time. +struct Dimensions(BTreeMap); + +impl Visit for Dimensions { + fn record_i64(&mut self, field: &Field, value: i64) { + self.0.insert(field.name().to_owned(), value as i128); + } + fn record_u64(&mut self, field: &Field, value: u64) { + self.0.insert(field.name().to_owned(), value as i128); + } + fn record_i128(&mut self, field: &Field, value: i128) { + self.0.insert(field.name().to_owned(), value); + } + fn record_u128(&mut self, field: &Field, value: u128) { + self.0.insert(field.name().to_owned(), value as i128); + } + fn record_bool(&mut self, field: &Field, value: bool) { + self.0.insert(field.name().to_owned(), value as i128); + } + fn record_debug(&mut self, _field: &Field, _value: &dyn std::fmt::Debug) {} +} + +/// A tracing `Layer` that emits `warp.profile.v1` JSON records on +/// span close. Thread-safe: wraps the writer in a `Mutex`. +pub struct JsonLayer { + writer: Mutex, +} + +impl JsonLayer { + pub fn new(writer: W) -> Self { + Self { + writer: Mutex::new(writer), + } + } +} + +impl Layer for JsonLayer +where + S: Subscriber + for<'a> LookupSpan<'a>, + W: Write + Send + 'static, +{ + fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { + let mut dims = Dimensions(BTreeMap::new()); + attrs.record(&mut dims); + if let Some(span) = ctx.span(id) { + span.extensions_mut().insert(dims); + } + } + + fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { + let Some(span) = ctx.span(id) else { return }; + let start = SpanStart { + wall: Instant::now(), + cpu_ns: timing::thread_cpu_ns(), + rss_bytes: rss::peak_rss_bytes(), + counters: counters::snapshot(), + }; + span.extensions_mut().insert(start); + } + + fn on_close(&self, id: Id, ctx: Context<'_, S>) { + let Some(span) = ctx.span(&id) else { return }; + let ext = span.extensions(); + let Some(start) = ext.get::() else { + return; + }; + + let wall_ns = start.wall.elapsed().as_nanos() as u64; + let cpu_ns = match (timing::thread_cpu_ns(), start.cpu_ns) { + (Some(end), Some(begin)) => Some(end.saturating_sub(begin)), + _ => None, + }; + let rss_delta = match (rss::peak_rss_bytes(), start.rss_bytes) { + (Some(end), Some(begin)) => Some(end.saturating_sub(begin) as i64), + _ => None, + }; + let delta = counters::snapshot().delta(&start.counters); + + let dims_default = Dimensions(BTreeMap::new()); + let dims = ext.get::().unwrap_or(&dims_default); + + let mut out = Vec::with_capacity(256); + let _ = write!(out, "{{\"schema\":\"warp.profile.v1\",\"phase\":"); + write_json_string(&mut out, span.name()); + let _ = write!(out, ",\"wall_ns\":{wall_ns}"); + if let Some(c) = cpu_ns { + let _ = write!(out, ",\"cpu_ns\":{c}"); + } + if let Some(r) = rss_delta { + let _ = write!(out, ",\"rss_delta_bytes\":{r}"); + } + + // counters + let _ = write!(out, ",\"counters\":{{"); + let mut first = true; + for (c, v) in delta.iter_nonzero() { + if !first { + let _ = write!(out, ","); + } + first = false; + let _ = write!(out, "\"{}\":{}", c.name(), v); + } + let _ = write!(out, "}}"); + + // dimensions + let _ = write!(out, ",\"dimensions\":{{"); + let mut first = true; + for (k, v) in &dims.0 { + if !first { + let _ = write!(out, ","); + } + first = false; + write_json_string(&mut out, k); + let _ = write!(out, ":{v}"); + } + let _ = writeln!(out, "}}}}"); + + if let Ok(mut w) = self.writer.lock() { + let _ = w.write_all(&out); + } + // ignore counter on unused-import warning for Counter when delta is empty + let _ = Counter::ALL.len(); + } +} + +fn write_json_string(out: &mut Vec, s: &str) { + out.push(b'"'); + for b in s.bytes() { + match b { + b'"' => out.extend_from_slice(b"\\\""), + b'\\' => out.extend_from_slice(b"\\\\"), + b'\n' => out.extend_from_slice(b"\\n"), + b'\r' => out.extend_from_slice(b"\\r"), + b'\t' => out.extend_from_slice(b"\\t"), + 0x00..=0x1F => { + let _ = write!(out, "\\u{b:04x}"); + } + _ => out.push(b), + } + } + out.push(b'"'); +} diff --git a/src/profile/mod.rs b/src/profile/mod.rs new file mode 100644 index 0000000..575ceaf --- /dev/null +++ b/src/profile/mod.rs @@ -0,0 +1,84 @@ +//! Profile instrumentation. +//! +//! The library always emits `tracing` spans at phase boundaries; this +//! module installs a subscriber that renders those spans. Rendering is +//! off by default — the `profile` cargo feature is required to bring in +//! `tracing-subscriber` and `libc` (for `clock_gettime` / `getrusage`). +//! +//! Two sinks are provided: +//! * [`init()`] / [`init_fmt()`] — human-readable text on stderr. +//! * [`init_json(writer)`] — newline-delimited JSON records to any +//! `io::Write` sink (stderr, a file, a pipe). Schema: +//! `warp.profile.v1`. See [`layer`] for the field list. +//! +//! Each installs a global subscriber the first time it is called; later +//! calls are no-ops. Without the feature, every init function returns +//! `false` and records nothing. + +pub mod counters; +pub mod rss; +pub mod timing; + +#[cfg(feature = "profile")] +pub mod layer; + +/// Install a human-readable stderr subscriber (mimics the old +/// `[PROFILE]` lines). No-op without the `profile` feature. +#[cfg(feature = "profile")] +pub fn init() -> bool { + init_fmt() +} + +#[cfg(not(feature = "profile"))] +pub fn init() -> bool { + false +} + +/// Human-readable fmt subscriber on stderr. Reads `RUST_LOG` to pick a +/// filter; defaults to `warp=info`. +#[cfg(feature = "profile")] +pub fn init_fmt() -> bool { + use tracing_subscriber::{fmt, prelude::*, EnvFilter}; + let env_filter = + EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warp=info")); + tracing_subscriber::registry() + .with(env_filter) + .with( + fmt::layer() + .with_target(false) + .with_span_events(fmt::format::FmtSpan::CLOSE) + .with_writer(std::io::stderr), + ) + .try_init() + .is_ok() +} + +#[cfg(not(feature = "profile"))] +pub fn init_fmt() -> bool { + false +} + +/// Install the JSON subscriber. One `warp.profile.v1` record per closed +/// span is written to `writer`. Reads `RUST_LOG` like [`init_fmt`]. +#[cfg(feature = "profile")] +pub fn init_json(writer: W) -> bool +where + W: std::io::Write + Send + 'static, +{ + use tracing_subscriber::{prelude::*, EnvFilter}; + let env_filter = + EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warp=info")); + tracing_subscriber::registry() + .with(env_filter) + .with(layer::JsonLayer::new(writer)) + .try_init() + .is_ok() +} + +#[cfg(not(feature = "profile"))] +pub fn init_json(_writer: W) -> bool +where + W: std::io::Write + Send + 'static, +{ + false +} diff --git a/src/profile/rss.rs b/src/profile/rss.rs new file mode 100644 index 0000000..0d253fa --- /dev/null +++ b/src/profile/rss.rs @@ -0,0 +1,42 @@ +//! Peak resident-set-size accounting via `getrusage(RUSAGE_SELF)`. +//! +//! Linux reports `ru_maxrss` in kilobytes; macOS reports it in bytes. We +//! normalise to bytes. The kernel tracks the peak RSS for the process (not +//! the current thread), so this value is monotonic — useful only via +//! deltas across intervals. +//! +//! Returns `None` if the syscall fails. +//! +//! Without the `profile` feature this module compiles to stubs that always +//! return `None`. + +#[cfg(feature = "profile")] +pub fn peak_rss_bytes() -> Option { + #[cfg(any(target_os = "linux", target_os = "macos"))] + { + // SAFETY: rusage is a POD; the kernel writes the full struct on success. + let mut ru: libc::rusage = unsafe { std::mem::zeroed() }; + let rc = unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut ru) }; + if rc != 0 { + return None; + } + let maxrss = ru.ru_maxrss as u64; + #[cfg(target_os = "macos")] + { + Some(maxrss) // bytes + } + #[cfg(target_os = "linux")] + { + Some(maxrss.saturating_mul(1024)) // kilobytes -> bytes + } + } + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + { + None + } +} + +#[cfg(not(feature = "profile"))] +pub fn peak_rss_bytes() -> Option { + None +} diff --git a/src/profile/timing.rs b/src/profile/timing.rs new file mode 100644 index 0000000..e0b5e64 --- /dev/null +++ b/src/profile/timing.rs @@ -0,0 +1,38 @@ +//! CPU-time accounting for the current thread. +//! +//! Wall time is available from `std::time::Instant`. CPU time is not — we +//! need `clock_gettime(CLOCK_THREAD_CPUTIME_ID)` (Linux / macOS Monterey+) +//! or `CLOCK_PROCESS_CPUTIME_ID` (older macOS) or `GetThreadTimes` +//! (Windows; not supported here). +//! +//! Returns `None` if the platform clock isn't available. +//! +//! Without the `profile` feature this module compiles to stubs that always +//! return `None`. + +#[cfg(feature = "profile")] +pub fn thread_cpu_ns() -> Option { + #[cfg(any(target_os = "linux", target_os = "macos"))] + { + let mut ts = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + // SAFETY: timespec is a POD; the kernel writes to it on success. + let rc = unsafe { libc::clock_gettime(libc::CLOCK_THREAD_CPUTIME_ID, &mut ts) }; + if rc == 0 { + Some((ts.tv_sec as u64).saturating_mul(1_000_000_000) + (ts.tv_nsec as u64)) + } else { + None + } + } + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + { + None + } +} + +#[cfg(not(feature = "profile"))] +pub fn thread_cpu_ns() -> Option { + None +} diff --git a/src/protocol/oracle.rs b/src/protocol/oracle.rs index 9248e47..966582f 100644 --- a/src/protocol/oracle.rs +++ b/src/protocol/oracle.rs @@ -20,6 +20,8 @@ use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; use ark_std::log2; use std::cell::OnceCell; +use crate::count_ops; + /// A Warp oracle: a committed codeword together with its lazily-materialised /// multilinear extension. pub struct Oracle { @@ -57,13 +59,16 @@ impl Oracle { /// Index query: `f[i]`. pub fn query_at_leaf(&self, idx: usize) -> F { + count_ops!(OracleLeafQueries); self.evals[idx] } /// Point query on the multilinear extension: `\hat f(ζ)` for /// `ζ ∈ F^{log n}`. Materialises the MLE on first call and caches it. pub fn query_at_point(&self, point: &[F]) -> F { + count_ops!(OraclePointQueries); let mle = self.mle.get_or_init(|| { + count_ops!(MleMaterializations); let log_n = log2(self.evals.len()) as usize; DenseMultilinearExtension::from_evaluations_slice(log_n, &self.evals) }); diff --git a/src/protocol/phases/batching.rs b/src/protocol/phases/batching.rs index b977424..c1ee7ba 100644 --- a/src/protocol/phases/batching.rs +++ b/src/protocol/phases/batching.rs @@ -22,6 +22,7 @@ use efficient_sumcheck::{ }; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; +use crate::count_ops; use crate::protocol::oracle::Oracle; use crate::utils::poly::eq_poly; @@ -82,6 +83,8 @@ where // call efficient sumcheck for batched_constraint checks let alpha = { let _s = tracing::info_span!("batching.sumcheck").entered(); + let log_n = ark_std::log2(n) as u64; + count_ops!(BatchingRounds, log_n); inner_product_sumcheck( &mut oracle.evals().to_vec(), &mut batched_constraint_poly(&ood_evals_vec, &id_non_0_eval_sums), diff --git a/src/protocol/phases/ood.rs b/src/protocol/phases/ood.rs index 4e29945..591cc94 100644 --- a/src/protocol/phases/ood.rs +++ b/src/protocol/phases/ood.rs @@ -8,6 +8,7 @@ use ark_ff::{Field, PrimeField}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; +use crate::count_ops; use crate::protocol::oracle::Oracle; /// Output of the OOD phase: the flat challenge vector and the prover's @@ -32,6 +33,7 @@ where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { let samples_flat = prover_state.verifier_messages_vec::(s * log_n); + count_ops!(OodPointQueries, s as u64); let answers = samples_flat .chunks(log_n) .map(|zeta| oracle.query_at_point(zeta)) diff --git a/src/protocol/phases/pesat.rs b/src/protocol/phases/pesat.rs index 766c93f..f65f631 100644 --- a/src/protocol/phases/pesat.rs +++ b/src/protocol/phases/pesat.rs @@ -21,6 +21,7 @@ use ark_crypto_primitives::{ use ark_ff::{Field, PrimeField}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; +use crate::count_ops; use crate::crypto::merkle::build_codeword_leaves; use crate::error::ProverError; use crate::types::PesatOutput; @@ -48,6 +49,7 @@ where // a. encode witnesses let (codewords, leaves) = { let _s = tracing::info_span!("pesat.encode").entered(); + count_ops!(EncodeCalls, witnesses.len() as u64); build_codeword_leaves(code, witnesses, l1) }; @@ -57,6 +59,7 @@ where // c. commit to witnesses let td_0 = { let _s = tracing::info_span!("pesat.merkle_commit").entered(); + count_ops!(MerkleTreeBuilds); MerkleTree::::new( mt_leaf_hash_params, mt_two_to_one_hash_params, diff --git a/src/protocol/phases/proximity.rs b/src/protocol/phases/proximity.rs index e1e2876..814658e 100644 --- a/src/protocol/phases/proximity.rs +++ b/src/protocol/phases/proximity.rs @@ -16,6 +16,7 @@ use ark_crypto_primitives::{ }; use ark_ff::Field; +use crate::count_ops; use crate::crypto::merkle::compute_auth_paths; use crate::error::VerifierError; use crate::protocol::query::QueryIndices; @@ -55,11 +56,16 @@ where { let auth_0 = { let _s = tracing::info_span!("proximity.auth_0").entered(); + count_ops!(MerklePathsGenerated, queries.leaf_positions.len() as u64); compute_auth_paths(td_0, &queries.leaf_positions)? }; let auth_j = { let _s = tracing::info_span!("proximity.auth_j").entered(); + count_ops!( + MerklePathsGenerated, + (acc_td.len() * queries.leaf_positions.len()) as u64 + ); acc_td .iter() .map(|td| compute_auth_paths(td, &queries.leaf_positions)) @@ -118,6 +124,7 @@ where (path.leaf_index == queries.leaf_positions[i]) .ok_or_err(VerifierError::ShiftQueryIndex)?; + count_ops!(MerklePathsVerified); let is_valid = path.verify( mt_leaf_hash_params, mt_two_to_one_hash_params, @@ -134,6 +141,7 @@ where for (j, path) in paths.iter().enumerate() { (path.leaf_index == queries.leaf_positions[j]) .ok_or_err(VerifierError::ShiftQueryIndex)?; + count_ops!(MerklePathsVerified); let is_valid = path.verify( mt_leaf_hash_params, mt_two_to_one_hash_params, diff --git a/src/protocol/phases/twin_constraint.rs b/src/protocol/phases/twin_constraint.rs index 779ee8f..80ef2b1 100644 --- a/src/protocol/phases/twin_constraint.rs +++ b/src/protocol/phases/twin_constraint.rs @@ -27,6 +27,7 @@ use efficient_sumcheck::{ }; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; +use crate::count_ops; use crate::protocol::oracle::Oracle; use crate::relations::r1cs::R1CSConstraints; use crate::types::AccumulatorInstance; @@ -195,6 +196,7 @@ where // c. run the sumcheck let sc = { let _s = tracing::info_span!("twin_constraint.sumcheck").entered(); + count_ops!(TwinConstraintRounds, log_l as u64); coefficient_sumcheck(&evaluator, &mut tablewise, &mut pw, log_l, prover_state) }; debug_assert_eq!(sc.verifier_messages.len(), log_l); diff --git a/tests/profile_json.rs b/tests/profile_json.rs new file mode 100644 index 0000000..0e6bbf2 --- /dev/null +++ b/tests/profile_json.rs @@ -0,0 +1,169 @@ +//! End-to-end check that Plan O's JSON layer emits well-formed +//! `warp.profile.v1` records with phase names, dimensions, and non-zero +//! op counters. +//! +//! Runs only under `--features profile` (see the `cfg` below). Without +//! the feature there's no JSON layer to test. + +#![cfg(feature = "profile")] + +use std::io::{self, Write}; +use std::sync::{Arc, Mutex}; + +use ark_bls12_381::Fr as BLS12_381; +use ark_codes::{ + reed_solomon::{config::ReedSolomonConfig, ReedSolomon}, + traits::LinearCode, +}; +use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; +use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; +use ark_std::rand::thread_rng; +use ark_std::UniformRand; +use std::marker::PhantomData; + +use warp::config::WARPConfig; +use warp::relations::{ + r1cs::{ + hashchain::{compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness}, + R1CS, + }, + BundledPESAT, Relation, ToPolySystem, +}; +use warp::traits::AccumulationScheme; +use warp::types::{AccumulatorInstance, AccumulatorWitness}; +use warp::utils::poseidon; +use warp::WARP; + +/// `Arc>>` wrapped so it implements `io::Write`. +#[derive(Clone)] +struct SharedBuf(Arc>>); + +impl Write for SharedBuf { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.lock().unwrap().extend_from_slice(buf); + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[test] +fn json_layer_emits_phase_records() { + let sink = SharedBuf(Arc::new(Mutex::new(Vec::new()))); + let installed = warp::profile::init_json(sink.clone()); + assert!(installed, "json subscriber install should succeed on first call"); + + // Minimum viable prove run — same shape as the top-level warp_test. + let l1 = 4; + let s = 8; + let t = 7; + let hash_chain_size = 10; + let mut rng = thread_rng(); + let poseidon_config = poseidon::initialize_poseidon_config::(); + let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + poseidon_config.clone(), + hash_chain_size, + )) + .unwrap(); + let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code = ReedSolomon::new(code_config.clone()); + + let (instances, witnesses): (Vec<_>, Vec<_>) = (0..l1) + .map(|_| { + let preimage = vec![BLS12_381::rand(&mut rng)]; + let instance = HashChainInstance { + digest: compute_hash_chain::>( + &poseidon_config, + &preimage, + hash_chain_size, + ), + }; + let witness = HashChainWitness { + preimage, + _crhs_scheme: PhantomData::>, + }; + let relation = HashChainRelation::, CRHGadget<_>>::new( + instance, + witness, + (poseidon_config.clone(), hash_chain_size), + ); + (relation.x, relation.w) + }) + .unzip(); + + let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); + let hash_chain_warp = + WARP::, _, Blake3MerkleConfig>::new( + warp_config, + code, + r1cs.clone(), + (), + (), + ); + + let domainsep = spongefish::domain_separator!("test::profile_json"); + let mut prover_state = domainsep.instance(&0u32).std_prover(); + + hash_chain_warp + .prove( + (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + &mut prover_state, + witnesses, + instances, + AccumulatorInstance::empty(), + AccumulatorWitness::empty(), + ) + .unwrap(); + + // Inspect collected records. + let bytes = sink.0.lock().unwrap().clone(); + let text = String::from_utf8(bytes).expect("JSON output is UTF-8"); + assert!(!text.is_empty(), "JSON sink should contain at least one record"); + + let lines: Vec<&str> = text.lines().collect(); + assert!( + !lines.is_empty(), + "expected newline-delimited JSON, got: {text:?}" + ); + + // Every line is a record carrying the schema tag. + for (i, line) in lines.iter().enumerate() { + assert!( + line.contains(r#""schema":"warp.profile.v1""#), + "line {i} missing schema: {line}" + ); + assert!(line.contains(r#""wall_ns""#), "line {i} missing wall_ns: {line}"); + assert!(line.contains(r#""counters""#), "line {i} missing counters: {line}"); + assert!( + line.contains(r#""dimensions""#), + "line {i} missing dimensions: {line}" + ); + } + + // Every top-level phase must appear at least once. + for phase in [ + "warp.prove", + "pesat", + "twin_constraint", + "ood", + "batching", + "proximity", + ] { + let needle = format!(r#""phase":"{phase}""#); + assert!( + text.contains(&needle), + "expected a record for phase `{phase}`, got lines: {lines:#?}" + ); + } + + // At least one record must have non-empty counters (pesat bumps several). + assert!( + text.contains(r#""merkle_tree_builds":"#), + "expected merkle_tree_builds counter in output: {text}" + ); + assert!( + text.contains(r#""encode_calls":"#), + "expected encode_calls counter in output: {text}" + ); +} From dfbab4a2c9b1ce676e62c9d2838e6e5146f7933e Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Sat, 18 Apr 2026 14:03:46 +0200 Subject: [PATCH 11/68] Plan B v1: iai-callgrind bench harness + Mac-Docker wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a deterministic instruction-count bench for WARP::prove, intended as the regression signal for a future CI gate. Complements the existing criterion wall-time bench (which stays informational). - benches/iai_phases.rs: one library_benchmark for `prove` at the unit-test parameter shape (l1=4, s=2, t=7, hashchain=10). v1 scope is intentionally narrow — the docstring explains why the parameterised `#[bench::case(setup = ...)]` form didn't compile under iai-callgrind 0.14 from this crate's bench root. Plumbing more sizes is deferred. - Cargo.toml: iai-callgrind 0.14 as a dev-dep, second `[[bench]]` entry wired with `harness = false`. - benches/docker/Dockerfile.iai: slim Debian image with valgrind + the iai-callgrind-runner binary pre-installed, for macOS hosts where valgrind has been unsupported since Big Sur. - Makefile: `bench-wall` (criterion, any host), `bench-ci` (iai natively, Linux/valgrind required), `bench-ci-local` (builds and runs the Docker image with cargo registry/git/target caches mounted from target/iai-docker-cache/ so arkworks isn't redownloaded each run). Plus `test` and `clippy` convenience targets. - benches/README.md: explains the split (noisy wall time vs deterministic instruction count), installation, Docker pathway, and v1 scope limits. Deferred (tracked in the plan): multiple parameter points, baseline capture + commit, and the GitHub Actions workflow that gates PRs. Those come as small separate commits so the CI change can be reviewed on its own. Verification: - cargo check / --features profile : clean - cargo build --bench iai_phases : clean (run needs valgrind) - cargo build --bench warp_rs : clean - cargo clippy --all-features : clean - cargo test : 14/14 (unchanged) --- Cargo.toml | 11 ++++ Makefile | 48 +++++++++++++++ benches/README.md | 62 +++++++++++++++++++ benches/docker/Dockerfile.iai | 26 ++++++++ benches/iai_phases.rs | 108 ++++++++++++++++++++++++++++++++++ 5 files changed, 255 insertions(+) create mode 100644 Makefile create mode 100644 benches/README.md create mode 100644 benches/docker/Dockerfile.iai create mode 100644 benches/iai_phases.rs diff --git a/Cargo.toml b/Cargo.toml index d388d68..5f0a9bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,11 @@ spongefish = { git = "https://github.com/z-tech/spongefish.git", branch = "small ark-bls12-381 = "0.5.0" ark-bn254 = "0.5.0" criterion = "0.8" +# Deterministic instruction-count benches. Requires valgrind + the +# `iai-callgrind-runner` binary at runtime (see benches/README.md). +# Unused on macOS except via Docker — `cargo build --bench iai_phases` +# still works everywhere, only `cargo bench` fails without valgrind. +iai-callgrind = "0.14" [features] default = ["asm"] @@ -63,3 +68,9 @@ profile = ["dep:tracing-subscriber", "dep:libc"] [[bench]] name = "warp_rs" harness = false + +# iai-callgrind: deterministic instruction counts per prove phase. Runs +# under valgrind; use `make bench-ci-local` on macOS to run inside Docker. +[[bench]] +name = "iai_phases" +harness = false diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..91be7a9 --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +# Convenience targets for Plan B (benchmarking + regression detection). +# Plan 0 / O deliverables are consumed via cargo directly — no make target +# needed. + +.PHONY: help test clippy bench-wall bench-ci bench-ci-local bench-docker-build + +help: + @echo "Targets:" + @echo " test cargo test (both feature configs)" + @echo " clippy cargo clippy --all-targets --all-features" + @echo " bench-wall criterion wall-time benches (runs anywhere)" + @echo " bench-ci iai-callgrind instruction-count benches (Linux native)" + @echo " bench-ci-local iai-callgrind benches inside Docker (macOS-friendly)" + +test: + cargo test + cargo test --features profile + +clippy: + cargo clippy --all-targets -- -D warnings + cargo clippy --all-targets --all-features -- -D warnings + +bench-wall: + cargo bench --bench warp_rs + +# Native iai-callgrind bench. Requires valgrind + iai-callgrind-runner +# installed on the host. Will fail on macOS (valgrind is unsupported +# since Big Sur) — use `bench-ci-local` instead. +bench-ci: + cargo bench --bench iai_phases + +IAI_IMAGE := warp-iai-bench +IAI_CACHE := $(CURDIR)/target/iai-docker-cache + +bench-docker-build: + docker build -f benches/docker/Dockerfile.iai -t $(IAI_IMAGE) . + +# Run the iai bench inside a Linux container. Mounts the repo read-write +# so the target/ dir (including bench output) is reused across runs. +# Caches cargo registry + git + target in host-side directories to avoid +# re-downloading arkworks on every invocation. +bench-ci-local: bench-docker-build + mkdir -p $(IAI_CACHE)/registry $(IAI_CACHE)/git + docker run --rm \ + -v $(CURDIR):/workspace \ + -v $(IAI_CACHE)/registry:/usr/local/cargo/registry \ + -v $(IAI_CACHE)/git:/usr/local/cargo/git \ + $(IAI_IMAGE) diff --git a/benches/README.md b/benches/README.md new file mode 100644 index 0000000..7b0f118 --- /dev/null +++ b/benches/README.md @@ -0,0 +1,62 @@ +# Warp benches + +Two bench suites with different roles: + +| Bench | Signal | Scope | Cost | +|--------------------------|--------------------------|----------------------------|------------------| +| `warp_rs` (criterion) | Wall time, noisy | Whole `prove` at 5 sizes | Fast, informational | +| `iai_phases` (iai-callgrind) | Instruction count, deterministic | Whole `prove` at 1 size (v1) | Slow, CI gate | + +The criterion suite is for local feedback — it reports wall time in +milliseconds, which is human-intuitive but varies across machines and +loads. It runs natively on any host. + +The iai-callgrind suite is for **regression detection**. Callgrind counts +executed instructions, so the number is reproducible across CI runs. A +1% change is real signal. Plan B uses it as the PR gate. + +## Running + +### Criterion (works on macOS, Linux, Windows) + +```bash +make bench-wall +# or +cargo bench --bench warp_rs +``` + +### iai-callgrind (native — requires valgrind) + +```bash +cargo install iai-callgrind-runner --version 0.14.0 +make bench-ci +# or +cargo bench --bench iai_phases +``` + +### iai-callgrind (macOS via Docker) + +Valgrind hasn't worked on macOS since Big Sur, so on a Mac host run the +bench inside a Linux container: + +```bash +make bench-ci-local +``` + +This builds `benches/docker/Dockerfile.iai` (cached after the first +run), mounts the repo read-write, and caches cargo registry + git in +`target/iai-docker-cache/` so subsequent runs don't re-download +arkworks. First run: ~5 min build + ~5 min bench. Later runs: ~30 s +build check + bench time. + +## v1 scope + +`iai_phases` currently benches **one** prove configuration +(`l1=4, s=2, t=7, hashchain=10`). Larger parameter points and +per-phase attribution are deferred — see the docstring at the top of +`iai_phases.rs` for why. + +Baseline instruction counts are not yet committed; the plan is to +capture them in a follow-up once the CI workflow is wired up. See +`~/.claude/plans/nested-conjuring-scott.md` → Plan B for the full +roadmap, and the TODO about a GitHub Actions workflow. diff --git a/benches/docker/Dockerfile.iai b/benches/docker/Dockerfile.iai new file mode 100644 index 0000000..ff92433 --- /dev/null +++ b/benches/docker/Dockerfile.iai @@ -0,0 +1,26 @@ +# Dockerfile for running iai-callgrind benches on hosts without valgrind +# (primarily macOS). Build + run via the repo-root Makefile: +# +# make bench-ci-local +# +# The image caches the iai-callgrind-runner binary + valgrind so repeat +# runs only re-build the warp crate. +FROM rust:1.81-slim-bookworm + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + valgrind \ + pkg-config \ + libssl-dev \ + ca-certificates \ + git \ + && rm -rf /var/lib/apt/lists/* + +# Match the version in Cargo.toml dev-dependencies. +ARG IAI_RUNNER_VERSION=0.14.0 +RUN cargo install iai-callgrind-runner --version ${IAI_RUNNER_VERSION} --locked + +WORKDIR /workspace + +# Default: run the bench. The repo is expected to be mounted at /workspace. +CMD ["cargo", "bench", "--bench", "iai_phases"] diff --git a/benches/iai_phases.rs b/benches/iai_phases.rs new file mode 100644 index 0000000..2e9a976 --- /dev/null +++ b/benches/iai_phases.rs @@ -0,0 +1,108 @@ +//! iai-callgrind benchmarks measuring deterministic instruction counts +//! per prove call at fixed parameter points. +//! +//! Unlike [`benches/warp_rs.rs`] (criterion, wall time), these numbers +//! are reproducible across machines: callgrind counts executed +//! instructions, not time. A 1% change is real signal. +//! +//! Runs under valgrind. On macOS valgrind is not available natively — +//! use `make bench-ci-local` which invokes Docker (see benches/README.md). +//! +//! Installation: +//! cargo install iai-callgrind-runner --version 0.14.0 +//! then +//! cargo bench --bench iai_phases +//! +//! **v1 scope**: one size (l1=4, hashchain=10). Setup (encoding, +//! poseidon config build, instance gen) is measured inside the bench +//! because plumbing the `setup = expr` attribute through +//! parameterised `#[bench::...]` cases didn't resolve cleanly on +//! iai-callgrind 0.14 — the macro failed to see the target function +//! from the bench-crate root. Revisit when adding more parameter +//! points; a working pattern is either the non-parameterised form +//! below, or `iai_callgrind::BinaryBenchmarkConfig`. + +use ark_bls12_381::Fr as BLS12_381; +use ark_codes::{ + reed_solomon::{config::ReedSolomonConfig, ReedSolomon}, + traits::LinearCode, +}; +use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; +use ark_std::rand::thread_rng; + +use iai_callgrind::{black_box, library_benchmark, library_benchmark_group, main}; +use warp::config::WARPConfig; +use warp::relations::BundledPESAT; +use warp::traits::AccumulationScheme as _; +use warp::types::{AccumulatorInstance, AccumulatorWitness}; +use warp::WARP; + +mod utils; +use utils::domainsep::init_prover_state; +use utils::hash_chain::{get_hashchain_instance_witness_pairs, get_hashchain_r1cs}; +use utils::poseidon; + +type F = BLS12_381; + +/// Output of [`setup_prove`]: everything needed to call `warp.prove` once, +/// assembled in setup time (NOT counted in the measurement). +struct ProveInputs { + warp: WARP, ReedSolomon, Blake3MerkleConfig>, + pk: (warp::relations::r1cs::R1CS, usize, usize, usize), + prover_state: spongefish::ProverState, + instances: Vec>, + witnesses: Vec>, +} + +fn setup_prove(l: usize, s: usize, t: usize, hashchain_size: usize) -> ProveInputs { + let mut rng = thread_rng(); + let poseidon_config = poseidon::initialize_poseidon_config::(); + let r1cs = get_hashchain_r1cs(&poseidon_config, hashchain_size); + + let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code = ReedSolomon::new(code_config); + + let warp_config = WARPConfig::new(l, l, s, t, r1cs.config(), code.code_len()); + let warp = WARP::>::new(warp_config, code, r1cs.clone(), (), ()); + + let (instances, witnesses) = + get_hashchain_instance_witness_pairs(l, &poseidon_config, hashchain_size, &mut rng); + + ProveInputs { + warp, + pk: (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + prover_state: init_prover_state(), + instances, + witnesses, + } +} + +fn run_prove(mut inputs: ProveInputs) { + black_box( + inputs + .warp + .prove( + inputs.pk, + &mut inputs.prover_state, + inputs.witnesses, + inputs.instances, + AccumulatorInstance::empty(), + AccumulatorWitness::empty(), + ) + .unwrap(), + ); +} + +// Smallest configuration — matches the unit-test shape (l1=4, hashchain=10). +#[library_benchmark] +fn bench_prove_small() { + let inputs = setup_prove(4, 2, 7, 10); + run_prove(black_box(inputs)); +} + +library_benchmark_group!( + name = prove_benches; + benchmarks = bench_prove_small +); + +main!(library_benchmark_groups = prove_benches); From 9fb6fe179b3e92b02c08bc1ea863ee0a2808626a Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Sat, 18 Apr 2026 14:23:08 +0200 Subject: [PATCH 12/68] Plan P: soundness-driven parameter selection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds src/params/ — given (λ, |F|, code rate, list-decoding regime), pick the minimum (s, t) that achieves λ bits of soundness on a Reed–Solomon code. - types: Regime::{Provable, Conjectured}, SecurityLevel, Params, SoundnessBound, ParamError. SoundnessBound::meets(λ) answers the "is this enough?" question directly. - select(λ, field_bits, code_rate, regime) → Params. Uses the Johnson-bound proximity-query formula (provable: t ≥ 2λ/log₂(1/ρ), conjectured: t ≥ λ/log₂(1/ρ)), with a field-admissibility check (log₂|F| ≥ λ + 40) to ensure polylog noise terms are negligible. - validate(params, field_bits, code_rate, regime, target): the inverse — computes the achieved soundness and reports per-term admissibility so callers can see partial failures. - presets::PRESETS: canonical (λ, rate, regime) → (s, t) rows for 80/128-bit targets at common rates (1/2, 1/8). A test enforces that every row matches `select` output, so drift between the table and the formulas is caught at build time. - src/bin/warp-params.rs: dependency-free CLI with `select`, `validate`, and `table` subcommands. Dumps PRESETS as TSV and exits non-zero if a validate call doesn't meet the target. - docs/paper-mods/mod4_parameter_selection.tex: replaces the stub with the full derivation, citing STIR / WHIR for the proximity- gap bounds and marking the deferred items (batching-sumcheck calibration of s, non-RS codes, a reference table with matching proofs). The hard-coded `s=8, t=7` in warp_test is intentionally unchanged for this pass — those are functional-test shapes, not security values. A later pass can thread PRESETS through callers that care about real targets. Verification: - 23 tests pass (14 original + 9 new in params::tests) - cargo clippy, both feature configs: clean - ./target/debug/warp-params table prints PRESETS; select / validate round-trip; validate correctly rejects insufficient (s, t) --- docs/paper-mods/mod4_parameter_selection.tex | 91 ++++++-- src/bin/warp-params.rs | 232 +++++++++++++++++++ src/lib.rs | 1 + src/params/mod.rs | 148 ++++++++++++ src/params/presets.rs | 101 ++++++++ src/params/select.rs | 54 +++++ src/params/types.rs | 86 +++++++ src/params/validate.rs | 43 ++++ 8 files changed, 741 insertions(+), 15 deletions(-) create mode 100644 src/bin/warp-params.rs create mode 100644 src/params/mod.rs create mode 100644 src/params/presets.rs create mode 100644 src/params/select.rs create mode 100644 src/params/types.rs create mode 100644 src/params/validate.rs diff --git a/docs/paper-mods/mod4_parameter_selection.tex b/docs/paper-mods/mod4_parameter_selection.tex index f755d99..1ec203b 100644 --- a/docs/paper-mods/mod4_parameter_selection.tex +++ b/docs/paper-mods/mod4_parameter_selection.tex @@ -1,29 +1,90 @@ \documentclass{article} \input{notation.tex} -\title{Modification 4: \\ Soundness-driven parameter selection (stub)} +\title{Modification 4: \\ Soundness-driven parameter selection} \author{Warp paper-mods} \date{} \begin{document} \maketitle -\paragraph{Status.} Stub. Full spec authored in Plan P. Paired code: -\codemod{src/params/} (to be created). +\paragraph{Paired Rust module.} \codemod{src/params/}, with a CLI front-end +at \codemod{src/bin/warp-params.rs}. -\section*{Abstract} +\section{Motivation} -The paper states security bounds symbolically; the implementation currently -hard-codes parameter values (e.g.\ \(s = 8\), \(t = 7\)) with no derivation -record. Modification 4 introduces, both in the paper and in code, an -\emph{inverse} to the soundness analysis: given a security target -\(\lambda\), a field \(\F\), a code rate \(\rho\), and an accumulation -depth, produce the minimum-cost parameter tuple \((s, t, l, l_1)\) hitting -that target. Both list-decoding regimes (provable and conjectured) are -supported via a user toggle. +The paper states the soundness bound of Warp symbolically; the implementation +has so far hard-coded parameter values (for instance, \(s = 8\), \(t = 7\) +in the regression tests). There is no record of which security target those +choices correspond to, nor is there a way to ask the code for the minimum +parameter tuple that hits a chosen target \(\lambda\). -The code module \codemod{src/params/} will expose pure \texttt{select} and -\texttt{validate} functions plus a preset table to replace the ad-hoc -constants currently appearing in tests. See Plan P for the full treatment. +Modification 4 adds, on both sides, an \emph{inverse} to the soundness +analysis. Given +\(\lambda \in \mathbb{N}\), field size \(\size{\F}\), Reed--Solomon code rate +\(\rho = k/n\), and a choice of \emph{list-decoding regime}, produce the +smallest parameter tuple \((s,\ t)\) that provably (or conjecturally) gives +\(\lambda\) bits of soundness. The workload parameters \((l,\ l_1)\) remain +caller-chosen; Modification 4 does not select them. + +\section{Regimes} + +\begin{description} + \item[\IOR{Provable}.] Each proximity query rejects a word at distance + \(e\) from the nearest codeword with probability at least \(e\). + Taking the Johnson bound \(e = 1 - \sqrt{\rho}\) as the largest + value supported by a proof for Reed--Solomon codes, the soundness + error of \(t\) independent proximity queries is at most + \(\bigl(1 - e\bigr)^{t} = \rho^{t/2}\). Therefore + \[ + t \;\geq\; \frac{2\lambda}{\log_2(1/\rho)}. + \] + \item[\IOR{Conjectured}.] The list-decodability conjecture for + Reed--Solomon codes extends the argument up to radius + \(e = 1 - \rho\). The same calculation gives + \[ + t \;\geq\; \frac{\lambda}{\log_2(1/\rho)}. + \] +\end{description} + +For \(s\) (OOD samples) we use the conservative constant \(s = 8\), +which dominates the round-complexity of batching without materially +affecting soundness at the scales we run. A tighter bound is deferred to +a follow-up; the current code documents this choice explicitly. + +\section{Field-size admissibility} + +Several other soundness terms are each at most +\(\mathrm{polylog}(\cdot) / \size{\F}\) (sumcheck rounds, zero-check +randomness absorbs, transcript-squeezed challenges). They are negligible +provided \(\log_2 \size{\F} \geq \lambda + \epsilon\) for a small constant +\(\epsilon\); the code rejects any choice that violates this admissibility. + +\section{References used, and scope limits} + +The formulas above are the standard proximity-gap / list-decoding bounds +from the Reed--Solomon STARK lineage, in the precise formulation used in +STIR~\cite{STIR} and WHIR~\cite{WHIR}. They are \emph{not} a full +re-derivation of the Warp paper's soundness proof; they capture the +dominant term (proximity queries) and ignore polylogarithmic noise +controlled by the field-size check. The Rust module +\codemod{src/params/} uses the same formulas and marks every constant +it uses, so refining against the paper's exact proof reduces to editing +one table. + +\paragraph{Deferred.} Calibration of \(s\) against the batching-sumcheck +soundness, derivation for non-Reed--Solomon codes, and a reference +table of attested parameter tuples with matching proofs. + +\begin{thebibliography}{9} +\bibitem{STIR} + Arnon, Chiesa, Fenzi, Yogev. + \emph{STIR: Reed--Solomon Proximity Testing with Fewer Queries.} + CRYPTO 2024. +\bibitem{WHIR} + Arnon, Chiesa, Fenzi, Yogev. + \emph{WHIR: Reed--Solomon Proximity Testing with Super-Fast Verification.} + 2024. +\end{thebibliography} \end{document} diff --git a/src/bin/warp-params.rs b/src/bin/warp-params.rs new file mode 100644 index 0000000..77f3508 --- /dev/null +++ b/src/bin/warp-params.rs @@ -0,0 +1,232 @@ +//! `warp-params` — CLI for exploring parameter selection. +//! +//! Paired spec: `docs/paper-mods/mod4_parameter_selection.tex`. +//! +//! Usage: +//! +//! ```text +//! warp-params select --lambda 128 --rate 0.5 --field-bits 254 --regime conjectured +//! warp-params validate --s 8 --t 128 --lambda 128 --rate 0.5 --field-bits 254 --regime conjectured +//! warp-params table # dump PRESETS as a TSV +//! ``` +//! +//! This is intentionally dependency-free (no clap). Keeps the crate slim +//! and the exit semantics simple. + +use std::process::ExitCode; + +use warp::params::{ + lookup, select, validate, ParamError, Params, Regime, SecurityLevel, PRESETS, +}; + +fn main() -> ExitCode { + let args: Vec = std::env::args().skip(1).collect(); + match args.first().map(String::as_str) { + Some("select") => cmd_select(&args[1..]), + Some("validate") => cmd_validate(&args[1..]), + Some("table") => cmd_table(), + Some("-h") | Some("--help") | Some("help") | None => { + print_usage(); + ExitCode::SUCCESS + } + Some(other) => { + eprintln!("warp-params: unknown subcommand `{other}`"); + print_usage(); + ExitCode::from(2) + } + } +} + +fn print_usage() { + eprintln!( + "warp-params — pick soundness-driven WARP parameters.\n\ + \n\ + Usage:\n\ + warp-params select --lambda N --rate NUM/DEN|FLOAT --field-bits N --regime provable|conjectured\n\ + warp-params validate --s N --t N --lambda N --rate ... --field-bits N --regime ...\n\ + warp-params table\n\ + \n\ + See docs/paper-mods/mod4_parameter_selection.tex for the derivation." + ); +} + +fn cmd_select(args: &[String]) -> ExitCode { + let flags = match parse_flags(args) { + Ok(f) => f, + Err(e) => { + eprintln!("warp-params select: {e}"); + return ExitCode::from(2); + } + }; + let (lambda, rate, field_bits, regime) = match (flags.lambda, flags.rate, flags.field_bits, flags.regime) { + (Some(l), Some(r), Some(fb), Some(rg)) => (SecurityLevel(l), r, fb, rg), + _ => { + eprintln!("warp-params select: need --lambda, --rate, --field-bits, --regime"); + return ExitCode::from(2); + } + }; + match select(lambda, field_bits, rate, regime) { + Ok(p) => { + // Prefer a preset if we happen to have one on file — lets + // users see the canonical attested row, not just a recomputed + // tuple. (The two are identical by `presets_match_select_output`.) + if let Some(preset) = lookup(lambda, rate, regime) { + println!( + "λ={} rate={:.4} regime={:?} → s={} t={} (preset)", + preset.lambda.bits(), + preset.code_rate(), + preset.regime, + preset.params.s, + preset.params.t + ); + } else { + println!( + "λ={} rate={:.4} regime={:?} → s={} t={}", + lambda.bits(), + rate, + regime, + p.s, + p.t + ); + } + ExitCode::SUCCESS + } + Err(e) => { + eprintln!("warp-params select: {}", format_err(e)); + ExitCode::from(1) + } + } +} + +fn cmd_validate(args: &[String]) -> ExitCode { + let flags = match parse_flags(args) { + Ok(f) => f, + Err(e) => { + eprintln!("warp-params validate: {e}"); + return ExitCode::from(2); + } + }; + let (s, t, lambda, rate, field_bits, regime) = match ( + flags.s, + flags.t, + flags.lambda, + flags.rate, + flags.field_bits, + flags.regime, + ) { + (Some(s), Some(t), Some(l), Some(r), Some(fb), Some(rg)) => { + (s, t, SecurityLevel(l), r, fb, rg) + } + _ => { + eprintln!("warp-params validate: need --s, --t, --lambda, --rate, --field-bits, --regime"); + return ExitCode::from(2); + } + }; + let params = Params { s, t }; + match validate(¶ms, field_bits, rate, regime, lambda) { + Ok(bound) => { + println!( + "proximity_bits={:.2} field_admissible={} ood_admissible={} meets_target={}", + bound.proximity_bits, + bound.field_admissible, + bound.ood_admissible, + bound.meets(lambda), + ); + if bound.meets(lambda) { + ExitCode::SUCCESS + } else { + ExitCode::from(1) + } + } + Err(e) => { + eprintln!("warp-params validate: {}", format_err(e)); + ExitCode::from(1) + } + } +} + +fn cmd_table() -> ExitCode { + println!("lambda\trate\tregime\ts\tt"); + for preset in PRESETS { + println!( + "{}\t{}/{}\t{:?}\t{}\t{}", + preset.lambda.bits(), + preset.code_rate_num, + preset.code_rate_den, + preset.regime, + preset.params.s, + preset.params.t, + ); + } + ExitCode::SUCCESS +} + +#[derive(Default)] +struct Flags { + lambda: Option, + rate: Option, + field_bits: Option, + regime: Option, + s: Option, + t: Option, +} + +fn parse_flags(args: &[String]) -> Result { + let mut flags = Flags::default(); + let mut it = args.iter(); + while let Some(flag) = it.next() { + let value = it + .next() + .ok_or_else(|| format!("missing value for {flag}"))?; + match flag.as_str() { + "--lambda" => flags.lambda = Some(parse_u32(value)?), + "--rate" => flags.rate = Some(parse_rate(value)?), + "--field-bits" => flags.field_bits = Some(parse_u32(value)?), + "--regime" => flags.regime = Some(parse_regime(value)?), + "--s" => flags.s = Some(parse_u32(value)? as usize), + "--t" => flags.t = Some(parse_u32(value)? as usize), + other => return Err(format!("unknown flag: {other}")), + } + } + Ok(flags) +} + +fn parse_u32(s: &str) -> Result { + s.parse().map_err(|e| format!("expected u32, got `{s}`: {e}")) +} + +fn parse_rate(s: &str) -> Result { + if let Some((num, den)) = s.split_once('/') { + let n: f64 = num.parse().map_err(|e| format!("rate num `{num}`: {e}"))?; + let d: f64 = den.parse().map_err(|e| format!("rate den `{den}`: {e}"))?; + if d == 0.0 { + return Err("rate denominator must be non-zero".into()); + } + Ok(n / d) + } else { + s.parse().map_err(|e| format!("rate `{s}`: {e}")) + } +} + +fn parse_regime(s: &str) -> Result { + match s.to_ascii_lowercase().as_str() { + "provable" | "prov" => Ok(Regime::Provable), + "conjectured" | "conj" => Ok(Regime::Conjectured), + other => Err(format!( + "unknown regime `{other}`, want `provable` or `conjectured`" + )), + } +} + +fn format_err(e: ParamError) -> String { + match e { + ParamError::InvalidRate => "rate must be in (0, 1)".to_string(), + ParamError::FieldTooSmall { field_bits, lambda } => { + format!( + "field is only {field_bits} bits; \ + a target of {lambda} bits requires at least {} bits", + lambda + warp::params::select::FIELD_EPSILON + ) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 827273a..b26c788 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,7 @@ pub mod config; pub mod constraints; pub mod crypto; pub mod error; +pub mod params; pub mod profile; pub mod protocol; pub mod relations; diff --git a/src/params/mod.rs b/src/params/mod.rs new file mode 100644 index 0000000..937dbc9 --- /dev/null +++ b/src/params/mod.rs @@ -0,0 +1,148 @@ +//! Soundness-driven parameter selection for WARP. +//! +//! Paired spec: `docs/paper-mods/mod4_parameter_selection.tex`. +//! +//! Given a security target, a field, a Reed–Solomon code rate, and a +//! choice of list-decoding regime, [`select`] returns the smallest +//! `(s, t)` tuple that achieves the target. [`validate`] is the inverse +//! and reports the soundness of an already-chosen tuple. +//! +//! The workload parameters `l` and `l1` are **not** chosen here — they +//! are caller-driven by the batch size the application actually needs. +//! +//! Derivation limits are called out in the companion `.tex`; the +//! formulas capture the dominant proximity-query term only. + +pub mod presets; +pub mod select; +pub mod types; +pub mod validate; + +pub use presets::{lookup, Preset, PRESETS}; +pub use select::select; +pub use types::{ParamError, Params, Regime, SecurityLevel, SoundnessBound}; +pub use validate::validate; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn select_validate_roundtrip_provable() { + let lambda = SecurityLevel::STANDARD_128; + let p = select(lambda, 254, 0.5, Regime::Provable).unwrap(); + assert!( + p.t >= 256, + "provable t at λ=128, ρ=1/2 should need ≥ 256 queries, got {}", + p.t + ); + let bound = validate(&p, 254, 0.5, Regime::Provable, lambda).unwrap(); + assert!( + bound.meets(lambda), + "selected params should self-validate: {bound:?}" + ); + } + + #[test] + fn select_validate_roundtrip_conjectured() { + let lambda = SecurityLevel::STANDARD_128; + let p = select(lambda, 254, 0.5, Regime::Conjectured).unwrap(); + // Conjectured halves the query count at ρ=0.5 (2 vs 1 bits per query). + assert!(p.t >= 128 && p.t < 256); + let bound = validate(&p, 254, 0.5, Regime::Conjectured, lambda).unwrap(); + assert!(bound.meets(lambda)); + } + + #[test] + fn smaller_rate_needs_fewer_queries() { + let lambda = SecurityLevel::STANDARD_128; + let p_half = select(lambda, 254, 0.5, Regime::Provable).unwrap(); + let p_eighth = select(lambda, 254, 0.125, Regime::Provable).unwrap(); + assert!( + p_eighth.t < p_half.t, + "ρ=1/8 needs fewer queries than ρ=1/2: {} vs {}", + p_eighth.t, + p_half.t + ); + } + + #[test] + fn conjectured_needs_fewer_queries_than_provable() { + let lambda = SecurityLevel::STANDARD_128; + let p_prov = select(lambda, 254, 0.5, Regime::Provable).unwrap(); + let p_conj = select(lambda, 254, 0.5, Regime::Conjectured).unwrap(); + assert!(p_conj.t < p_prov.t); + } + + #[test] + fn field_too_small_is_rejected() { + // 64-bit field, 128-bit target: should fail admissibility. + assert!(matches!( + select(SecurityLevel::STANDARD_128, 64, 0.5, Regime::Provable), + Err(ParamError::FieldTooSmall { .. }) + )); + } + + #[test] + fn invalid_rate_is_rejected() { + let lambda = SecurityLevel::STANDARD_80; + assert_eq!( + select(lambda, 254, 0.0, Regime::Provable), + Err(ParamError::InvalidRate) + ); + assert_eq!( + select(lambda, 254, 1.0, Regime::Conjectured), + Err(ParamError::InvalidRate) + ); + } + + #[test] + fn presets_self_validate() { + // Every preset should achieve its claimed lambda under its regime. + for preset in PRESETS { + // Use 254-bit field so field_admissible passes at 128-bit. + let bound = validate( + &preset.params, + 254, + preset.code_rate(), + preset.regime, + preset.lambda, + ) + .unwrap(); + assert!( + bound.meets(preset.lambda), + "preset {:?} rate={:?} regime={:?} fails its own target: {:?}", + preset.lambda, + preset.code_rate(), + preset.regime, + bound + ); + } + } + + #[test] + fn presets_match_select_output() { + for preset in PRESETS { + let recomputed = + select(preset.lambda, 254, preset.code_rate(), preset.regime).unwrap(); + assert_eq!( + recomputed, preset.params, + "preset drift: {:?} rate={:?} regime={:?}", + preset.lambda, + preset.code_rate(), + preset.regime + ); + } + } + + #[test] + fn lookup_round_trips() { + let p = lookup( + SecurityLevel::STANDARD_128, + 0.5, + Regime::Conjectured, + ) + .unwrap(); + assert_eq!(p.params.t, 128); + } +} diff --git a/src/params/presets.rs b/src/params/presets.rs new file mode 100644 index 0000000..b62ba75 --- /dev/null +++ b/src/params/presets.rs @@ -0,0 +1,101 @@ +//! Pre-computed parameter tuples for common `(λ, code_rate)` points. +//! +//! Each entry is derived by [`super::select`] at the named regime and +//! stored here as a `const` lookup so tests and CLIs don't need to re-run +//! the derivation on every invocation. If you change the bounds in +//! `mod4_parameter_selection.tex`, regenerate this table via +//! `cargo run --bin warp-params -- table`. + +use super::types::{Params, Regime, SecurityLevel}; + +/// One row of [`PRESETS`]. +pub struct Preset { + pub lambda: SecurityLevel, + pub code_rate_num: u32, + pub code_rate_den: u32, + pub regime: Regime, + pub params: Params, +} + +impl Preset { + pub fn code_rate(&self) -> f64 { + self.code_rate_num as f64 / self.code_rate_den as f64 + } +} + +/// Common `(λ, rate, regime) → (s, t)` selections. See module docstring +/// on regeneration. +/// +/// All entries use the minimum `s = 8` (see +/// `mod4_parameter_selection.tex` §2); `t` is the smallest integer that +/// meets the target under the named regime. +pub const PRESETS: &[Preset] = &[ + // λ=80 @ rate 1/2 + Preset { + lambda: SecurityLevel::STANDARD_80, + code_rate_num: 1, + code_rate_den: 2, + regime: Regime::Provable, + params: Params { s: 8, t: 160 }, + }, + Preset { + lambda: SecurityLevel::STANDARD_80, + code_rate_num: 1, + code_rate_den: 2, + regime: Regime::Conjectured, + params: Params { s: 8, t: 80 }, + }, + // λ=80 @ rate 1/8 (three bits per query under provable) + Preset { + lambda: SecurityLevel::STANDARD_80, + code_rate_num: 1, + code_rate_den: 8, + regime: Regime::Provable, + params: Params { s: 8, t: 54 }, + }, + Preset { + lambda: SecurityLevel::STANDARD_80, + code_rate_num: 1, + code_rate_den: 8, + regime: Regime::Conjectured, + params: Params { s: 8, t: 27 }, + }, + // λ=128 @ rate 1/2 + Preset { + lambda: SecurityLevel::STANDARD_128, + code_rate_num: 1, + code_rate_den: 2, + regime: Regime::Provable, + params: Params { s: 8, t: 256 }, + }, + Preset { + lambda: SecurityLevel::STANDARD_128, + code_rate_num: 1, + code_rate_den: 2, + regime: Regime::Conjectured, + params: Params { s: 8, t: 128 }, + }, + // λ=128 @ rate 1/8 + Preset { + lambda: SecurityLevel::STANDARD_128, + code_rate_num: 1, + code_rate_den: 8, + regime: Regime::Provable, + params: Params { s: 8, t: 86 }, + }, + Preset { + lambda: SecurityLevel::STANDARD_128, + code_rate_num: 1, + code_rate_den: 8, + regime: Regime::Conjectured, + params: Params { s: 8, t: 43 }, + }, +]; + +/// Look up a preset by `(λ, code_rate, regime)`. Returns `None` if no +/// exact row matches; use [`super::select`] for arbitrary inputs. +pub fn lookup(lambda: SecurityLevel, code_rate: f64, regime: Regime) -> Option<&'static Preset> { + PRESETS.iter().find(|p| { + p.lambda == lambda && (p.code_rate() - code_rate).abs() < 1e-9 && p.regime == regime + }) +} diff --git a/src/params/select.rs b/src/params/select.rs new file mode 100644 index 0000000..56f11a1 --- /dev/null +++ b/src/params/select.rs @@ -0,0 +1,54 @@ +//! Soundness-driven parameter selection. +//! +//! Paired spec: `docs/paper-mods/mod4_parameter_selection.tex`. +//! +//! Computes the smallest `(s, t)` tuple that achieves the requested +//! security level under the chosen list-decoding regime. See the `.tex` +//! for the derivation and which bounds were used from the STIR / WHIR +//! literature. + +use super::types::{ParamError, Params, Regime, SecurityLevel}; + +/// Minimum OOD samples. Covers the constant term used in the current +/// derivation; tightening this against the batching-sumcheck soundness +/// is deferred — see `mod4_parameter_selection.tex` §2. +const S_MIN: usize = 8; + +/// Additive slack on the field-size admissibility check: we require +/// `log₂|F| ≥ λ + FIELD_EPSILON` so the polylog noise terms are +/// negligible at the target level. +pub const FIELD_EPSILON: u32 = 40; + +/// Select the minimum `(s, t)` achieving `lambda` bits of soundness for +/// a Reed–Solomon code of rate `code_rate` over a field of `field_bits`, +/// under the chosen regime. +/// +/// Returns `Err(ParamError::InvalidRate)` if `code_rate ∉ (0, 1)`, and +/// `Err(ParamError::FieldTooSmall)` if the field cannot cover the polylog +/// noise at the requested target (see §3 of the paper-mods spec). +pub fn select( + lambda: SecurityLevel, + field_bits: u32, + code_rate: f64, + regime: Regime, +) -> Result { + if !(0.0 < code_rate && code_rate < 1.0) { + return Err(ParamError::InvalidRate); + } + if field_bits < lambda.bits() + FIELD_EPSILON { + return Err(ParamError::FieldTooSmall { + field_bits, + lambda: lambda.bits(), + }); + } + + let bits_per_query = match regime { + Regime::Provable => 0.5 * (-code_rate.log2()), + Regime::Conjectured => -code_rate.log2(), + }; + debug_assert!(bits_per_query > 0.0); + + let t = (lambda.bits() as f64 / bits_per_query).ceil() as usize; + + Ok(Params { s: S_MIN, t }) +} diff --git a/src/params/types.rs b/src/params/types.rs new file mode 100644 index 0000000..c85e833 --- /dev/null +++ b/src/params/types.rs @@ -0,0 +1,86 @@ +//! Parameter-selection types. +//! +//! Paired spec: `docs/paper-mods/mod4_parameter_selection.tex`. + +/// Target soundness, in bits. `SecurityLevel(128)` means the soundness +/// error should be at most 2⁻¹²⁸. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct SecurityLevel(pub u32); + +impl SecurityLevel { + pub const STANDARD_80: Self = Self(80); + pub const STANDARD_100: Self = Self(100); + pub const STANDARD_128: Self = Self(128); + pub const STANDARD_192: Self = Self(192); + pub const STANDARD_256: Self = Self(256); + + pub fn bits(self) -> u32 { + self.0 + } +} + +/// Which list-decoding regime to assume when bounding proximity-query +/// soundness. See `docs/paper-mods/mod4_parameter_selection.tex` §2. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Regime { + /// Johnson bound — radius `1 − √ρ`. Soundness error per query is + /// `√ρ`; proven for Reed–Solomon in the STIR / WHIR lineage. + Provable, + /// Conjectured list-decodability up to radius `1 − ρ`. Soundness + /// error per query is `ρ`; halves the required query count vs. + /// provable at the same target. + Conjectured, +} + +/// Selected security-driven WARP parameters. +/// +/// Workload parameters (`l`, `l1`) are *not* chosen here — they're caller- +/// supplied based on the batch size the application needs. This struct +/// carries only the choices whose minimum is dictated by soundness. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Params { + /// OOD samples. + pub s: usize, + /// Proximity / shift queries. + pub t: usize, +} + +/// Result of [`crate::params::validate`]. Reports the log₂ soundness error +/// achievable under the given `(params, rate, regime)`, together with the +/// field-size admissibility check. +#[derive(Clone, Copy, Debug)] +pub struct SoundnessBound { + /// `-log₂` of the proximity-query soundness error contribution. + pub proximity_bits: f64, + /// Whether the field is large enough that the polylog-noise + /// contributions are negligible at the target level. + pub field_admissible: bool, + /// Whether the selected `s` meets the minimum OOD sample count + /// used in the current formulas. Currently always true with the + /// hard-coded `S_MIN`; kept as a field so a future refinement can + /// surface a failure. + pub ood_admissible: bool, +} + +impl SoundnessBound { + /// `true` iff every component check passes at the caller's target. + pub fn meets(&self, target: SecurityLevel) -> bool { + self.field_admissible + && self.ood_admissible + && self.proximity_bits >= target.bits() as f64 + } +} + +/// Reasons [`crate::params::select`] or [`crate::params::validate`] can +/// reject a configuration. Never panics. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ParamError { + /// Code rate was out of range `(0, 1)`. + InvalidRate, + /// Field is too small to support the target soundness even with + /// infinite queries. + FieldTooSmall { + field_bits: u32, + lambda: u32, + }, +} diff --git a/src/params/validate.rs b/src/params/validate.rs new file mode 100644 index 0000000..c0ce171 --- /dev/null +++ b/src/params/validate.rs @@ -0,0 +1,43 @@ +//! Soundness validation — the inverse of [`super::select`]. +//! +//! Given a `Params`, report how many bits of security it gives under the +//! chosen regime, and whether each component check passes. + +use super::select::FIELD_EPSILON; +use super::types::{ParamError, Params, Regime, SecurityLevel, SoundnessBound}; + +const S_MIN: usize = 8; + +/// Compute the soundness bound that `params` achieves on a rate-`code_rate` +/// Reed–Solomon code over a field of `field_bits`, under `regime`. +/// +/// Returns `Err(ParamError::InvalidRate)` if the rate is outside `(0, 1)`. +/// `FieldTooSmall` is not returned here — field admissibility is surfaced +/// on the returned [`SoundnessBound`] so callers can reason about partial +/// failures. +pub fn validate( + params: &Params, + field_bits: u32, + code_rate: f64, + regime: Regime, + target: SecurityLevel, +) -> Result { + if !(0.0 < code_rate && code_rate < 1.0) { + return Err(ParamError::InvalidRate); + } + + let bits_per_query = match regime { + Regime::Provable => 0.5 * (-code_rate.log2()), + Regime::Conjectured => -code_rate.log2(), + }; + + let proximity_bits = (params.t as f64) * bits_per_query; + let field_admissible = field_bits >= target.bits() + FIELD_EPSILON; + let ood_admissible = params.s >= S_MIN; + + Ok(SoundnessBound { + proximity_bits, + field_admissible, + ood_admissible, + }) +} From 695eac93cc7b960bdc18d726add2f452da5301ce Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Sat, 18 Apr 2026 14:39:11 +0200 Subject: [PATCH 13/68] Plan T v1: negative-path verifier tests + Fiat-Shamir audit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the two highest-value pieces of test hardening from the plan; the rest (proptest, golden serialization, xtask ref lint, runtime F-S harness) is deferred and tracked in the todo list. tests/verifier_negative.rs - Shared `make_fixture()` runs the full two-phase accumulation so l2 > 0 and every cleanly-reachable `VerifierError` variant is triggerable. - One test per tamperable field, each confirming the verifier rejects the proof with the *specific* expected error (not just "some error"): * CodeEvaluationPoint (α tamper) * CircuitEvaluationPoint (β.0 and β.1 tampers — two tests) * NumShiftQueries (truncate shift_query_answers) * ShiftQueryIndex (swap auth_0 paths) * ShiftQuery (tamper shift_query_answer value) * NumL2Instances (truncate auth_j) * Target (tamper μ) - A happy-path test keeps the fixture honest. - Docstring enumerates the variants we did NOT reach through single-field tampering (SpongeFish/ArkError wrap lower-level errors; NumSumcheckRounds is transcript-derived; SumcheckRound is unraised in current code). docs/audits/fiat_shamir.md - Ordered, line-for-line mapping of every prover-side transcript write to its verifier-side read. 25 steps, each with file:line links on both sides. - A "what reviewers should spot-check" section calls out the specific squeeze-before-absorb patterns F-S-soundness bugs tend to take. - Scope: this is a **manual** audit (compensating control). The runtime ordering harness that would make drift undetectable at CI time is deferred because it requires instrumenting spongefish; noted explicitly at the bottom of the doc. Verification: - 23/23 unit tests + 9/9 negative-path tests pass - cargo clippy, both feature configs: clean --- docs/audits/fiat_shamir.md | 89 ++++++++++++ tests/verifier_negative.rs | 281 +++++++++++++++++++++++++++++++++++++ 2 files changed, 370 insertions(+) create mode 100644 docs/audits/fiat_shamir.md create mode 100644 tests/verifier_negative.rs diff --git a/docs/audits/fiat_shamir.md b/docs/audits/fiat_shamir.md new file mode 100644 index 0000000..ecd1944 --- /dev/null +++ b/docs/audits/fiat_shamir.md @@ -0,0 +1,89 @@ +# Fiat–Shamir audit + +Status: **v1 manual audit**, current as of commit `9bf4f43`. Automated +runtime-ordering enforcement is deferred (see bottom). + +## What this document is + +An ordered, line-for-line mapping of every `prover_message(…)` / +`prover_messages(…)` call on the prover side to its matching +`prover_message()` / `prover_messages_vec(…)` call on the verifier +side, and similarly for every `verifier_message(…)` / +`verifier_messages_vec(…)` challenge squeeze. + +A Fiat–Shamir soundness flaw typically has a simple shape: a challenge +is squeezed **before** some prover message that should have influenced +it. This document lets a reviewer walk both sides in order and satisfy +themselves that every squeeze happens after every absorb that should +determine it. It is the compensating control for us not yet having a +runtime harness that asserts this automatically. + +## Transcript ordering + +| Step | Prover call | File:line | Verifier call | File:line | +|------|-------------|-----------|---------------|-----------| +| 1. Index — public params | `public_message(description)` | [src/lib.rs:113](../../src/lib.rs#L113) | (via `public_message` domain-sep, no explicit read) | — | +| 2. Index — `m` | `prover_message(m)` | [src/lib.rs:114](../../src/lib.rs#L114) | `prover_messages_vec` (absorbed as part of `vk`) | via `index()` | +| 3. Index — `n` | `prover_message(n)` | [src/lib.rs:115](../../src/lib.rs#L115) | " | " | +| 4. Index — `k` | `prover_message(k)` | [src/lib.rs:116](../../src/lib.rs#L116) | " | " | +| 5. Fresh instances `x_i` | `absorb_instances` → `prover_message` loop | [src/protocol/transcript/prover.rs:14](../../src/protocol/transcript/prover.rs#L14) | `prover_messages_vec(instance_len)` loop | [src/protocol/transcript/verifier.rs:25](../../src/protocol/transcript/verifier.rs#L25) | +| 6. Accumulator `rt[i]` | `prover_message(bytes)` | [src/protocol/transcript/prover.rs:28](../../src/protocol/transcript/prover.rs#L28) | `prover_message() -> [u8;32]` loop | [src/protocol/transcript/verifier.rs:49](../../src/protocol/transcript/verifier.rs#L49) | +| 7. Accumulator `α[i]` | `prover_message` loop | [src/protocol/transcript/prover.rs:33](../../src/protocol/transcript/prover.rs#L33) | `prover_messages_vec(log_n)` loop | [src/protocol/transcript/verifier.rs:55](../../src/protocol/transcript/verifier.rs#L55) | +| 8. Accumulator `μ[i]` | `prover_message` | [src/protocol/transcript/prover.rs:38](../../src/protocol/transcript/prover.rs#L38) | `prover_messages_vec(l2)` | [src/protocol/transcript/verifier.rs:58](../../src/protocol/transcript/verifier.rs#L58) | +| 9. Accumulator `τ[i]` | `prover_message` loop | [src/protocol/transcript/prover.rs:43](../../src/protocol/transcript/prover.rs#L43) | `prover_messages_vec(log_m)` loop | [src/protocol/transcript/verifier.rs:61](../../src/protocol/transcript/verifier.rs#L61) | +| 10. Accumulator `x[i]` | `prover_message` loop | [src/protocol/transcript/prover.rs:49](../../src/protocol/transcript/prover.rs#L49) | `prover_messages_vec(instance_len)` loop | [src/protocol/transcript/verifier.rs:65](../../src/protocol/transcript/verifier.rs#L65) | +| 11. Accumulator `η[i]` | `prover_message` | [src/protocol/transcript/prover.rs:54](../../src/protocol/transcript/prover.rs#L54) | `prover_messages_vec(l2)` | [src/protocol/transcript/verifier.rs:68](../../src/protocol/transcript/verifier.rs#L68) | +| 12. PESAT — `rt₀` | `prover_message(root_bytes)` | [src/protocol/phases/pesat.rs:78](../../src/protocol/phases/pesat.rs#L78) | `prover_message() -> [u8;32]` | [src/protocol/transcript/verifier.rs:111](../../src/protocol/transcript/verifier.rs#L111) | +| 13. PESAT — `μ_i` | `prover_messages(&mus)` | [src/protocol/phases/pesat.rs:79](../../src/protocol/phases/pesat.rs#L79) | `prover_messages_vec(l1)` | [src/protocol/transcript/verifier.rs:115](../../src/protocol/transcript/verifier.rs#L115) | +| 14. PESAT — τ squeeze | `verifier_messages_vec::(log_m)` × l1 | [src/protocol/phases/pesat.rs:82](../../src/protocol/phases/pesat.rs#L82) | `verifier_message::()` × (l1 · log_m) | [src/protocol/transcript/verifier.rs:121](../../src/protocol/transcript/verifier.rs#L121) | +| 15. Twin-constraint — ω | `verifier_message()` | [src/protocol/phases/twin_constraint.rs:160](../../src/protocol/phases/twin_constraint.rs#L160) | `verifier_message::()` | [src/protocol/transcript/verifier.rs:126](../../src/protocol/transcript/verifier.rs#L126) | +| 16. Twin-constraint — τ | `verifier_messages_vec::(log_l)` | [src/protocol/phases/twin_constraint.rs:161](../../src/protocol/phases/twin_constraint.rs#L161) | `verifier_message::()` × log_l | [src/protocol/transcript/verifier.rs:128](../../src/protocol/transcript/verifier.rs#L128) | +| 17. Twin-constraint — sumcheck | per round: coeffs absorbed, γ squeezed (inside `coefficient_sumcheck`) | [src/protocol/phases/twin_constraint.rs:202](../../src/protocol/phases/twin_constraint.rs#L202) | per round: `prover_messages_vec` coeffs, `verifier_message` γ | [src/protocol/transcript/verifier.rs:136](../../src/protocol/transcript/verifier.rs#L136) | +| 18. Post-TC — new root | `prover_message(td_root_bytes)` | [src/lib.rs:213](../../src/lib.rs#L213) | `prover_message() -> [u8;32]` | [src/protocol/transcript/verifier.rs:143](../../src/protocol/transcript/verifier.rs#L143) | +| 19. Post-TC — η | `prover_message(&eta)` | [src/lib.rs:214](../../src/lib.rs#L214) | `prover_message() -> F` | [src/protocol/transcript/verifier.rs:147](../../src/protocol/transcript/verifier.rs#L147) | +| 20. Post-TC — ν₀ | `prover_message(&nu_0)` | [src/lib.rs:215](../../src/lib.rs#L215) | `prover_message() -> F` | [src/protocol/transcript/verifier.rs:148](../../src/protocol/transcript/verifier.rs#L148) | +| 21. OOD — sample points | `verifier_messages_vec::(s·log_n)` | [src/protocol/phases/ood.rs:35](../../src/protocol/phases/ood.rs#L35) | `verifier_message::()` × (s · log_n) | [src/protocol/transcript/verifier.rs:154](../../src/protocol/transcript/verifier.rs#L154) | +| 22. OOD — answers | `prover_messages(&answers)` | [src/protocol/phases/ood.rs:41](../../src/protocol/phases/ood.rs#L41) | `prover_messages_vec(s)` | [src/protocol/transcript/verifier.rs:158](../../src/protocol/transcript/verifier.rs#L158) | +| 23. Proximity — query bytes | `verifier_messages_vec::<[u8;1]>(num_bytes)` via `QueryIndices::sample` | [src/protocol/query.rs:18](../../src/protocol/query.rs#L18) | `verifier_message::<[u8;1]>()` × num_bytes | [src/protocol/transcript/verifier.rs:166](../../src/protocol/transcript/verifier.rs#L166) | +| 24. Batching — ξ | `verifier_messages_vec::(log_r)` | [src/protocol/phases/batching.rs:61](../../src/protocol/phases/batching.rs#L61) | `verifier_message::()` × log_r | [src/protocol/transcript/verifier.rs:169](../../src/protocol/transcript/verifier.rs#L169) | +| 25. Batching — sumcheck | per round: `[a, b]` absorbed, α squeezed (inside `inner_product_sumcheck`) | [src/protocol/phases/batching.rs:93](../../src/protocol/phases/batching.rs#L93) | per round: `prover_messages() -> [F;2]`, `verifier_message()` α | [src/protocol/transcript/verifier.rs:176](../../src/protocol/transcript/verifier.rs#L176) | + +## What every reviewer should spot-check + +For each challenge squeeze, confirm that every prover message that +**defines** that challenge's semantic purpose has already been absorbed +above it. Examples: + +- **τ challenges (step 14).** Squeezed after the PESAT root and fresh + μ_i (steps 12–13). ✓ Both values determine which witness the prover + committed to; binding τ to them is required so the prover can't pick + τ after learning which witness is rejected. +- **ω challenge (step 15).** Squeezed after all prior PESAT state and + after τ. ✓ ω linearly combines two claims; if the prover could pick + ω *before* τ, they could cancel the two terms against a dishonest + witness. +- **Shift-query bytes (step 23).** Squeezed after the new commitment + `td_root_bytes`, η, ν₀, and the OOD answers (steps 18–22). ✓ The + query index must be unpredictable relative to the committed oracle. +- **Batching-sumcheck α challenges (step 25).** Squeezed per round + after each `[a, b]` is absorbed (inside the sumcheck). ✓ Standard. + +## What is **not** in this table + +- `domain_separator!("…")` invocations: those are audited via the + domain-sep macro's own per-call string matching; if the same + domainsep string is used on prover and verifier, the transcripts + align. Rotate the string if the layout changes. +- `AccumulatorInstance::empty()` / `AccumulatorWitness::empty()` + paths: these do nothing to the transcript, so they are + unaudited here. + +## Deferred — a runtime Fiat–Shamir harness + +Plan T originally proposed a test that captures the ordered list of +`prover_message` / `verifier_message` calls during a prove, and +asserts it matches a golden sequence. That would make drift between +prover and verifier impossible to land undetected. Implementing it +requires instrumenting `spongefish::ProverState` (external crate), so +it's deferred. The current table is the compensating control; it is +*manually* regenerated when any file above changes. diff --git a/tests/verifier_negative.rs b/tests/verifier_negative.rs new file mode 100644 index 0000000..fe44b3e --- /dev/null +++ b/tests/verifier_negative.rs @@ -0,0 +1,281 @@ +//! Negative-path verifier tests. +//! +//! The existing `warp_test` exercises only the happy path. This file +//! covers the other direction: for each cleanly-triggerable +//! [`warp::error::VerifierError`] variant, produce a valid proof, tamper +//! with one field, and assert the verifier rejects the proof with the +//! *specific* expected error. +//! +//! Catches a class of bug that's otherwise invisible: "verifier looks +//! correct on valid proofs but accepts broken ones." Several real-world +//! SNARKs have shipped that way. +//! +//! Not every error variant is reachable through a one-field tamper. +//! Variants we don't cover here: +//! +//! - `SpongeFish` / `ArkError` wrap underlying errors; hitting them +//! requires transcript-byte-level corruption rather than proof-object +//! tampering, which would exercise spongefish/arkworks, not our code. +//! - `NumSumcheckRounds` is derived from the transcript; a sibling of +//! `SpongeFish`. +//! - `SumcheckRound` is not raised from any code path right now. + +use std::marker::PhantomData; + +use ark_bls12_381::Fr as BLS12_381; +use ark_codes::{ + reed_solomon::{config::ReedSolomonConfig, ReedSolomon}, + traits::LinearCode, +}; +use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; +use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; +use ark_std::rand::thread_rng; +use ark_std::UniformRand; + +use warp::config::WARPConfig; +use warp::error::VerifierError; +use warp::relations::{ + r1cs::{ + hashchain::{compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness}, + R1CS, + }, + BundledPESAT, Relation, ToPolySystem, +}; +use warp::traits::AccumulationScheme; +use warp::types::{AccumulatorInstance, AccumulatorWitness, WARPProof}; +use warp::utils::poseidon; +use warp::WARP; + +type F = BLS12_381; +type MT = Blake3MerkleConfig; +type WarpT = WARP, ReedSolomon, MT>; + +/// Everything the verifier needs to re-check, plus enough dimensions +/// to re-derive the verifier state. +struct Fixture { + warp: WarpT, + vk: (usize, usize, usize), + acc_x: AccumulatorInstance, + proof: WARPProof, + narg_str: Vec, +} + +impl Fixture { + fn verify(&self, acc_x: AccumulatorInstance, proof: WARPProof) -> Result<(), VerifierError> { + let domainsep_v = spongefish::domain_separator!("test::warp::negative"); + let mut verifier_state = domainsep_v.instance(&0u32).std_verifier(&self.narg_str); + self.warp.verify(self.vk, &mut verifier_state, acc_x, proof) + } +} + +/// Build a real proof against the hash-chain relation, with both fresh +/// and accumulated components so negative tests can target any field. +fn make_fixture() -> Fixture { + let l1 = 4; + let s = 8; + let t = 7; + let hash_chain_size = 10; + let mut rng = thread_rng(); + let poseidon_config = poseidon::initialize_poseidon_config::(); + let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + poseidon_config.clone(), + hash_chain_size, + )) + .unwrap(); + let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code = ReedSolomon::new(code_config); + + let (instances, witnesses): (Vec<_>, Vec<_>) = (0..l1) + .map(|_| { + let preimage = vec![F::rand(&mut rng)]; + let instance = HashChainInstance { + digest: compute_hash_chain::>( + &poseidon_config, + &preimage, + hash_chain_size, + ), + }; + let witness = HashChainWitness { + preimage, + _crhs_scheme: PhantomData::>, + }; + let relation = HashChainRelation::, CRHGadget<_>>::new( + instance, + witness, + (poseidon_config.clone(), hash_chain_size), + ); + (relation.x, relation.w) + }) + .unzip(); + + // Phase 1: produce `l1` single-round acc states so we have a non-trivial + // accumulator to feed phase 2 (l2 > 0 so NumL2Instances is reachable). + let warp_cfg1 = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); + let w1 = WARP::, _, MT>::new(warp_cfg1, code.clone(), r1cs.clone(), (), ()); + + let (mut roots, mut alphas, mut mus, mut taus, mut xs, mut etas) = + (vec![], vec![], vec![], vec![], vec![], vec![]); + let (mut tds, mut fs, mut ws) = (vec![], vec![], vec![]); + + for _ in 0..l1 { + let ds = spongefish::domain_separator!("test::warp::negative"); + let mut ps = ds.instance(&0u32).std_prover(); + let ((acc_x, acc_w), _) = w1 + .prove( + (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + &mut ps, + witnesses.clone(), + instances.clone(), + AccumulatorInstance::empty(), + AccumulatorWitness::empty(), + ) + .unwrap(); + roots.push(acc_x.rt[0].clone()); + alphas.push(acc_x.alpha[0].clone()); + mus.push(acc_x.mu[0]); + taus.push(acc_x.beta.0[0].clone()); + xs.push(acc_x.beta.1[0].clone()); + etas.push(acc_x.eta[0]); + tds.push(acc_w.td[0].clone()); + fs.push(acc_w.f[0].clone()); + ws.push(acc_w.w[0].clone()); + } + + // Phase 2: the "real" prove with l2 > 0 accumulated instances. + let warp_cfg2 = WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); + let warp = WARP::, _, MT>::new(warp_cfg2, code, r1cs.clone(), (), ()); + + let ds = spongefish::domain_separator!("test::warp::negative"); + let mut ps = ds.instance(&0u32).std_prover(); + let ((acc_x, _acc_w), proof) = warp + .prove( + (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + &mut ps, + witnesses, + instances, + AccumulatorInstance { + rt: roots, + alpha: alphas, + mu: mus, + beta: (taus, xs), + eta: etas, + }, + AccumulatorWitness { + td: tds, + f: fs, + w: ws, + }, + ) + .unwrap(); + + Fixture { + warp, + vk: (r1cs.m, r1cs.n, r1cs.k), + acc_x, + proof, + narg_str: ps.narg_string().to_vec(), + } +} + +fn assert_err(result: Result<(), VerifierError>, expected: &str) { + match result { + Ok(()) => panic!("expected `{expected}`, got Ok(())"), + Err(err) => { + let dbg = format!("{err:?}"); + assert!( + dbg.contains(expected), + "expected `{expected}`, got `{dbg}`" + ); + } + } +} + +// Sanity check: a fresh fixture always verifies. +#[test] +fn happy_path_verifies() { + let fix = make_fixture(); + fix.verify(fix.acc_x.clone(), fix.proof.clone()) + .expect("untampered proof must verify"); +} + +#[test] +fn tampered_alpha_raises_code_evaluation_point() { + let fix = make_fixture(); + let mut acc_x = fix.acc_x.clone(); + acc_x.alpha[0][0] += F::from(1u64); + assert_err(fix.verify(acc_x, fix.proof.clone()), "CodeEvaluationPoint"); +} + +#[test] +fn tampered_beta_tau_raises_circuit_evaluation_point() { + let fix = make_fixture(); + let mut acc_x = fix.acc_x.clone(); + acc_x.beta.0[0][0] += F::from(1u64); + assert_err( + fix.verify(acc_x, fix.proof.clone()), + "CircuitEvaluationPoint", + ); +} + +#[test] +fn tampered_beta_x_raises_circuit_evaluation_point() { + let fix = make_fixture(); + let mut acc_x = fix.acc_x.clone(); + acc_x.beta.1[0][0] += F::from(1u64); + assert_err( + fix.verify(acc_x, fix.proof.clone()), + "CircuitEvaluationPoint", + ); +} + +#[test] +fn truncated_shift_query_answers_raises_num_shift_queries() { + let fix = make_fixture(); + let mut proof = fix.proof.clone(); + proof.shift_query_answers.pop(); + assert_err(fix.verify(fix.acc_x.clone(), proof), "NumShiftQueries"); +} + +#[test] +fn swapped_auth0_leaf_index_raises_shift_query_index() { + let fix = make_fixture(); + let mut proof = fix.proof.clone(); + // Overwrite auth_0[0]'s path with auth_0[1]'s path so leaf_index + // stops matching queries.leaf_positions[0]. + let p0_is_p1 = proof.auth_0[0].leaf_index == proof.auth_0[1].leaf_index; + if p0_is_p1 { + // Extremely unlikely but keeps the test deterministic: skip with a + // clear message rather than silently pass. + eprintln!("fixture happened to sample identical leaf indices; skipping"); + return; + } + proof.auth_0.swap(0, 1); + assert_err(fix.verify(fix.acc_x.clone(), proof), "ShiftQueryIndex"); +} + +#[test] +fn tampered_shift_query_answer_raises_shift_query() { + let fix = make_fixture(); + let mut proof = fix.proof.clone(); + // Each row of shift_query_answers has l2 + l1 entries; tampering any + // of them makes path.verify fail because the leaf hash no longer + // matches the committed root. + proof.shift_query_answers[0][0] += F::from(1u64); + assert_err(fix.verify(fix.acc_x.clone(), proof), "ShiftQuery"); +} + +#[test] +fn truncated_auth_j_raises_num_l2_instances() { + let fix = make_fixture(); + let mut proof = fix.proof.clone(); + proof.auth_j.pop(); + assert_err(fix.verify(fix.acc_x.clone(), proof), "NumL2Instances"); +} + +#[test] +fn tampered_mu_raises_target() { + let fix = make_fixture(); + let mut acc_x = fix.acc_x.clone(); + acc_x.mu[0] += F::from(1u64); + assert_err(fix.verify(acc_x, fix.proof.clone()), "Target"); +} From e6452ec978ba02e09323a764fd7773a6e2274a49 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Sat, 18 Apr 2026 15:00:48 +0200 Subject: [PATCH 14/68] Cleanup after Plans 0 / O / B / P / T MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five small fixups called out during the Plan T post-mortem. No behaviour changes — all tests keep passing on both feature configs. 1. Move the big `warp_test` / `warp_test_goldilocks` suites from `src/lib.rs` into `tests/integration_warp.rs`. They're end-to-end prove / verify / decide runs; keeping them as inline unit tests kept the file ~340 lines longer than necessary. `src/lib.rs` is now 500 lines (down from 844); the test content is unchanged. 2. Normalise phase-fn visibility to `pub`. Before, `pesat::prove` was `pub(crate)` while the other four phases' `prove` / `verify` / `verify_claim` entry points were `pub`. All output structs (`Oracle`, `OodOutput`, `BatchingOutput`, etc.) are already `pub`, so there was no reason for `pesat` to be the odd one out. `PesatOutput` promoted from `pub(crate)` → `pub` for the same reason. 3. Cross-reference lint audit — verified every module in `src/params`, `src/protocol/phases`, plus `src/protocol/oracle.rs` and `src/bin/warp-params.rs`, carries a doc-comment reference to its paired `docs/paper-mods/modN_*.tex`. No drift. 4. `presets::lookup` now takes an exact `(num, den)` pair instead of `f64` with an epsilon-compare. Preserves the caller's intent (`1/2` is distinct from `0.5`); the CLI plumbs a small `Rate` enum through parsing so ratios hit the preset table while bare decimals fall through to the computed-value branch. 5. Remove the `let _ = Counter::ALL.len()` silencer in `profile/layer.rs`. The import it was suppressing was an artifact of an earlier iteration and isn't needed now; dropping it and the `Counter` import lets clippy stay clean. Verification: - cargo test : 33/33 pass (22 unit + 2 integ + 9 negative) - cargo test --features profile : 34/34 pass (adds profile_json) - cargo clippy --all-features -Dwarnings : clean - warp-params select verified with both `1/2` and `0.5` inputs --- src/bin/warp-params.rs | 59 +++-- src/lib.rs | 344 ------------------------ src/params/mod.rs | 13 +- src/params/presets.rs | 15 +- src/profile/layer.rs | 4 +- src/protocol/phases/batching.rs | 2 +- src/protocol/phases/pesat.rs | 2 +- src/protocol/phases/proximity.rs | 2 +- src/protocol/phases/twin_constraint.rs | 2 +- src/types.rs | 2 +- tests/integration_warp.rs | 350 +++++++++++++++++++++++++ 11 files changed, 417 insertions(+), 378 deletions(-) create mode 100644 tests/integration_warp.rs diff --git a/src/bin/warp-params.rs b/src/bin/warp-params.rs index 77f3508..71d3662 100644 --- a/src/bin/warp-params.rs +++ b/src/bin/warp-params.rs @@ -65,16 +65,18 @@ fn cmd_select(args: &[String]) -> ExitCode { return ExitCode::from(2); } }; - match select(lambda, field_bits, rate, regime) { + match select(lambda, field_bits, rate.as_f64(), regime) { Ok(p) => { - // Prefer a preset if we happen to have one on file — lets - // users see the canonical attested row, not just a recomputed - // tuple. (The two are identical by `presets_match_select_output`.) - if let Some(preset) = lookup(lambda, rate, regime) { + // Prefer a preset if we have an exact-rational match on file — + // lets users see the canonical attested row, not just a + // recomputed tuple. (Equal by `presets_match_select_output`.) + let preset = rate.ratio().and_then(|(n, d)| lookup(lambda, n, d, regime)); + if let Some(preset) = preset { println!( - "λ={} rate={:.4} regime={:?} → s={} t={} (preset)", + "λ={} rate={}/{} regime={:?} → s={} t={} (preset)", preset.lambda.bits(), - preset.code_rate(), + preset.code_rate_num, + preset.code_rate_den, preset.regime, preset.params.s, preset.params.t @@ -83,7 +85,7 @@ fn cmd_select(args: &[String]) -> ExitCode { println!( "λ={} rate={:.4} regime={:?} → s={} t={}", lambda.bits(), - rate, + rate.as_f64(), regime, p.s, p.t @@ -123,7 +125,7 @@ fn cmd_validate(args: &[String]) -> ExitCode { } }; let params = Params { s, t }; - match validate(¶ms, field_bits, rate, regime, lambda) { + match validate(¶ms, field_bits, rate.as_f64(), regime, lambda) { Ok(bound) => { println!( "proximity_bits={:.2} field_admissible={} ood_admissible={} meets_target={}", @@ -164,13 +166,36 @@ fn cmd_table() -> ExitCode { #[derive(Default)] struct Flags { lambda: Option, - rate: Option, + rate: Option, field_bits: Option, regime: Option, s: Option, t: Option, } +/// Parsed rate that remembers whether it was written as `num/den` or as +/// a decimal. Exact-rational form enables preset lookup. +#[derive(Clone, Copy)] +enum Rate { + Ratio { num: u32, den: u32 }, + Float(f64), +} + +impl Rate { + fn as_f64(self) -> f64 { + match self { + Rate::Ratio { num, den } => num as f64 / den as f64, + Rate::Float(x) => x, + } + } + fn ratio(self) -> Option<(u32, u32)> { + match self { + Rate::Ratio { num, den } => Some((num, den)), + Rate::Float(_) => None, + } + } +} + fn parse_flags(args: &[String]) -> Result { let mut flags = Flags::default(); let mut it = args.iter(); @@ -195,16 +220,18 @@ fn parse_u32(s: &str) -> Result { s.parse().map_err(|e| format!("expected u32, got `{s}`: {e}")) } -fn parse_rate(s: &str) -> Result { +fn parse_rate(s: &str) -> Result { if let Some((num, den)) = s.split_once('/') { - let n: f64 = num.parse().map_err(|e| format!("rate num `{num}`: {e}"))?; - let d: f64 = den.parse().map_err(|e| format!("rate den `{den}`: {e}"))?; - if d == 0.0 { + let n: u32 = num.parse().map_err(|e| format!("rate num `{num}`: {e}"))?; + let d: u32 = den.parse().map_err(|e| format!("rate den `{den}`: {e}"))?; + if d == 0 { return Err("rate denominator must be non-zero".into()); } - Ok(n / d) + Ok(Rate::Ratio { num: n, den: d }) } else { - s.parse().map_err(|e| format!("rate `{s}`: {e}")) + s.parse::() + .map(Rate::Float) + .map_err(|e| format!("rate `{s}`: {e}")) } } diff --git a/src/lib.rs b/src/lib.rs index b26c788..3564783 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -498,347 +498,3 @@ impl< } } -#[cfg(test)] -pub mod test { - use super::AccumulationScheme; - use crate::serialize::{AccInstanceSerializer, AccWitnessSerializer, ProofSerializer}; - use crate::types::{AccumulatorInstance, AccumulatorWitness}; - use crate::{ - relations::{ - r1cs::{ - hashchain::{ - compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness, - }, - R1CS, - }, - BundledPESAT, Relation, ToPolySystem, - }, - utils::poseidon, - }; - use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; - - use ark_bls12_381::Fr as BLS12_381; - use ark_codes::{ - reed_solomon::{config::ReedSolomonConfig, ReedSolomon}, - traits::LinearCode, - }; - use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; - use ark_ff::UniformRand; - use ark_serialize::{CanonicalSerialize, Compress}; - use ark_std::rand::thread_rng; - - use std::marker::PhantomData; - - use super::{WARPConfig, WARP}; - - #[test] - pub fn warp_test() { - let l1 = 4; - let s = 8; - let t = 7; - let hash_chain_size = 10; - let mut rng = thread_rng(); - let poseidon_config = poseidon::initialize_poseidon_config::(); - let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( - poseidon_config.clone(), - hash_chain_size, - )) - .unwrap(); - let code_config = - ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); - let code = ReedSolomon::new(code_config.clone()); - - let instances_witnesses: (Vec>, Vec>) = (0..l1) - .map(|_| { - let preimage = vec![BLS12_381::rand(&mut rng)]; - let instance = HashChainInstance { - digest: compute_hash_chain::>( - &poseidon_config, - &preimage, - hash_chain_size, - ), - }; - let witness = HashChainWitness { - preimage, - _crhs_scheme: PhantomData::>, - }; - let relation = HashChainRelation::, CRHGadget<_>>::new( - instance, - witness, - (poseidon_config.clone(), hash_chain_size), - ); - (relation.x, relation.w) - }) - .unzip(); - - let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( - poseidon_config.clone(), - hash_chain_size, - )) - .unwrap(); - - let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = - WARP::, _, Blake3MerkleConfig>::new( - warp_config.clone(), - code.clone(), - r1cs.clone(), - (), - (), - ); - - let (mut acc_roots, mut acc_alphas, mut acc_mus, mut acc_taus, mut acc_xs, mut acc_eta) = - (vec![], vec![], vec![], vec![], vec![], vec![]); - let (mut acc_tds, mut acc_f, mut acc_ws) = (vec![], vec![], vec![]); - - for _ in 0..l1 { - let domainsep = spongefish::domain_separator!("test::warp"); - let mut prover_state = domainsep.instance(&0u32).std_prover(); - let ((acc_x, acc_w), _pf) = hash_chain_warp - .prove( - (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), - &mut prover_state, - instances_witnesses.1.clone(), - instances_witnesses.0.clone(), - AccumulatorInstance::empty(), - AccumulatorWitness::empty(), - ) - .unwrap(); - acc_roots.push(acc_x.rt[0].clone()); - acc_alphas.push(acc_x.alpha[0].clone()); - acc_mus.push(acc_x.mu[0]); - acc_taus.push(acc_x.beta.0[0].clone()); - acc_xs.push(acc_x.beta.1[0].clone()); - acc_eta.push(acc_x.eta[0]); - - acc_tds.push(acc_w.td[0].clone()); - acc_f.push(acc_w.f[0].clone()); - acc_ws.push(acc_w.w[0].clone()); - } - - let domainsep = spongefish::domain_separator!("test::warp"); - let warp_config = - WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); - - let hash_chain_warp = - WARP::, _, Blake3MerkleConfig>::new( - warp_config.clone(), - code.clone(), - r1cs.clone(), - (), - (), - ); - - let mut prover_state = domainsep.instance(&0u32).std_prover(); - let ((acc_x, acc_w), pf) = hash_chain_warp - .prove( - (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), - &mut prover_state, - instances_witnesses.1, - instances_witnesses.0, - AccumulatorInstance { - rt: acc_roots, - alpha: acc_alphas, - mu: acc_mus, - beta: (acc_taus, acc_xs), - eta: acc_eta, - }, - AccumulatorWitness { - td: acc_tds, - f: acc_f, - w: acc_ws, - }, - ) - .unwrap(); - - let narg_str = prover_state.narg_string().to_vec(); - let domainsep_v = spongefish::domain_separator!("test::warp"); - let mut verifier_state = domainsep_v.instance(&0u32).std_verifier(&narg_str); - hash_chain_warp - .verify( - (r1cs.m, r1cs.n, r1cs.k), - &mut verifier_state, - acc_x.clone(), - pf.clone(), - ) - .unwrap(); - hash_chain_warp - .decide(acc_w.clone(), acc_x.clone()) - .unwrap(); - - let acc_x_to_serde = AccInstanceSerializer::<_, Blake3MerkleConfig>::new(acc_x); - let acc_w_to_serde = AccWitnessSerializer::<_, Blake3MerkleConfig>::new(acc_w); - let proof_to_serde = ProofSerializer::new(pf); - - println!( - "acc_x size: {}", - acc_x_to_serde.serialized_size(Compress::Yes) - ); - println!( - "acc_w size: {}", - acc_w_to_serde.serialized_size(Compress::Yes) - ); - println!( - "proof size: {}", - proof_to_serde.serialized_size(Compress::Yes) - ); - println!("narg_str size: {}", narg_str.len()); - } - - #[test] - pub fn warp_test_goldilocks() { - use crate::utils::fields::Goldilocks; - - let l1 = 4; - let s = 8; - let t = 7; - let hash_chain_size = 10; - let mut rng = thread_rng(); - let poseidon_config = poseidon::initialize_poseidon_config::(); - let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( - poseidon_config.clone(), - hash_chain_size, - )) - .unwrap(); - let code_config = - ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); - let code = ReedSolomon::new(code_config); - - let instances_witnesses: (Vec>, Vec>) = (0..l1) - .map(|_| { - let preimage = vec![Goldilocks::rand(&mut rng)]; - let instance = HashChainInstance { - digest: compute_hash_chain::>( - &poseidon_config, - &preimage, - hash_chain_size, - ), - }; - let witness = HashChainWitness { - preimage, - _crhs_scheme: PhantomData::>, - }; - let relation = HashChainRelation::, CRHGadget<_>>::new( - instance, - witness, - (poseidon_config.clone(), hash_chain_size), - ); - (relation.x, relation.w) - }) - .unzip(); - - let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( - poseidon_config.clone(), - hash_chain_size, - )) - .unwrap(); - - let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = - WARP::, _, Blake3MerkleConfig>::new( - warp_config.clone(), - code.clone(), - r1cs.clone(), - (), - (), - ); - - let (mut acc_roots, mut acc_alphas, mut acc_mus, mut acc_taus, mut acc_xs, mut acc_eta) = - (vec![], vec![], vec![], vec![], vec![], vec![]); - let (mut acc_tds, mut acc_f, mut acc_ws) = (vec![], vec![], vec![]); - - for _ in 0..l1 { - let domainsep = spongefish::domain_separator!("test::warp"); - let mut prover_state = domainsep.instance(&0u32).std_prover(); - let ((acc_x, acc_w), _pf) = hash_chain_warp - .prove( - (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), - &mut prover_state, - instances_witnesses.1.clone(), - instances_witnesses.0.clone(), - AccumulatorInstance::empty(), - AccumulatorWitness::empty(), - ) - .unwrap(); - acc_roots.push(acc_x.rt[0].clone()); - acc_alphas.push(acc_x.alpha[0].clone()); - acc_mus.push(acc_x.mu[0]); - acc_taus.push(acc_x.beta.0[0].clone()); - acc_xs.push(acc_x.beta.1[0].clone()); - acc_eta.push(acc_x.eta[0]); - - acc_tds.push(acc_w.td[0].clone()); - acc_f.push(acc_w.f[0].clone()); - acc_ws.push(acc_w.w[0].clone()); - } - - let domainsep = spongefish::domain_separator!("test::warp"); - // Use 8 (2*l1) for the total accumulation size to test multi-instance accumulation - let warp_config = - WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); - - let hash_chain_warp = - WARP::, _, Blake3MerkleConfig>::new( - warp_config.clone(), - code.clone(), - r1cs.clone(), - (), - (), - ); - - let mut prover_state = domainsep.instance(&0u32).std_prover(); - let ((acc_x, acc_w), pf) = hash_chain_warp - .prove( - (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), - &mut prover_state, - instances_witnesses.1, - instances_witnesses.0, - AccumulatorInstance { - rt: acc_roots, - alpha: acc_alphas, - mu: acc_mus, - beta: (acc_taus, acc_xs), - eta: acc_eta, - }, - AccumulatorWitness { - td: acc_tds, - f: acc_f, - w: acc_ws, - }, - ) - .unwrap(); - - let narg_str = prover_state.narg_string().to_vec(); - let domainsep_v = spongefish::domain_separator!("test::warp"); - let mut verifier_state = domainsep_v.instance(&0u32).std_verifier(&narg_str); - hash_chain_warp - .verify( - (r1cs.m, r1cs.n, r1cs.k), - &mut verifier_state, - acc_x.clone(), - pf.clone(), - ) - .unwrap(); - hash_chain_warp - .decide(acc_w.clone(), acc_x.clone()) - .unwrap(); - - let acc_x_to_serde = AccInstanceSerializer::<_, Blake3MerkleConfig>::new(acc_x); - let acc_w_to_serde = AccWitnessSerializer::<_, Blake3MerkleConfig>::new(acc_w); - let proof_to_serde = ProofSerializer::new(pf); - - println!( - "Goldilocks acc_x size: {}", - acc_x_to_serde.serialized_size(Compress::Yes) - ); - println!( - "Goldilocks acc_w size: {}", - acc_w_to_serde.serialized_size(Compress::Yes) - ); - println!( - "Goldilocks proof size: {}", - proof_to_serde.serialized_size(Compress::Yes) - ); - println!("Goldilocks narg_str size: {}", narg_str.len()); - } -} diff --git a/src/params/mod.rs b/src/params/mod.rs index 937dbc9..fe6fb0d 100644 --- a/src/params/mod.rs +++ b/src/params/mod.rs @@ -137,12 +137,13 @@ mod tests { #[test] fn lookup_round_trips() { - let p = lookup( - SecurityLevel::STANDARD_128, - 0.5, - Regime::Conjectured, - ) - .unwrap(); + let p = lookup(SecurityLevel::STANDARD_128, 1, 2, Regime::Conjectured).unwrap(); assert_eq!(p.params.t, 128); } + + #[test] + fn lookup_misses_on_unknown_rate() { + // We have 1/2 and 1/8 on file; 1/4 is not a preset. + assert!(lookup(SecurityLevel::STANDARD_128, 1, 4, Regime::Provable).is_none()); + } } diff --git a/src/params/presets.rs b/src/params/presets.rs index b62ba75..a9f7366 100644 --- a/src/params/presets.rs +++ b/src/params/presets.rs @@ -92,10 +92,17 @@ pub const PRESETS: &[Preset] = &[ }, ]; -/// Look up a preset by `(λ, code_rate, regime)`. Returns `None` if no -/// exact row matches; use [`super::select`] for arbitrary inputs. -pub fn lookup(lambda: SecurityLevel, code_rate: f64, regime: Regime) -> Option<&'static Preset> { +/// Look up a preset by `(λ, num/den, regime)`. Exact-rational match — +/// callers that parsed the rate as a fraction preserve the exact form +/// and get a hit. Returns `None` if no exact row matches; use +/// [`super::select`] for arbitrary inputs. +pub fn lookup( + lambda: SecurityLevel, + num: u32, + den: u32, + regime: Regime, +) -> Option<&'static Preset> { PRESETS.iter().find(|p| { - p.lambda == lambda && (p.code_rate() - code_rate).abs() < 1e-9 && p.regime == regime + p.lambda == lambda && p.code_rate_num == num && p.code_rate_den == den && p.regime == regime }) } diff --git a/src/profile/layer.rs b/src/profile/layer.rs index 3dc7baa..98000ea 100644 --- a/src/profile/layer.rs +++ b/src/profile/layer.rs @@ -34,7 +34,7 @@ use tracing_subscriber::layer::Context; use tracing_subscriber::registry::LookupSpan; use tracing_subscriber::Layer; -use crate::profile::counters::{self, Counter, Snapshot}; +use crate::profile::counters::{self, Snapshot}; use crate::profile::{rss, timing}; /// What we stash on each span at enter-time. @@ -165,8 +165,6 @@ where if let Ok(mut w) = self.writer.lock() { let _ = w.write_all(&out); } - // ignore counter on unused-import warning for Counter when delta is empty - let _ = Counter::ALL.len(); } } diff --git a/src/protocol/phases/batching.rs b/src/protocol/phases/batching.rs index c1ee7ba..ab3c44d 100644 --- a/src/protocol/phases/batching.rs +++ b/src/protocol/phases/batching.rs @@ -103,7 +103,7 @@ where /// `[a, b]` where `h(X) = a·(1-2X) + b·X + (prev_target - b)·X²`; the /// caller's `a, b` unpacking mirrors `src/protocol/transcript/verifier.rs`. #[tracing::instrument(name = "batching.verify", skip_all)] -pub(crate) fn verify_claim(sigma_2: F, sums_per_round: Vec<[F; 2]>, alpha: &[F]) -> F +pub fn verify_claim(sigma_2: F, sums_per_round: Vec<[F; 2]>, alpha: &[F]) -> F where F: Field, { diff --git a/src/protocol/phases/pesat.rs b/src/protocol/phases/pesat.rs index f65f631..dad8f6f 100644 --- a/src/protocol/phases/pesat.rs +++ b/src/protocol/phases/pesat.rs @@ -32,7 +32,7 @@ use crate::types::PesatOutput; skip_all, fields(l1 = l1, log_m = log_m, n_witnesses = witnesses.len()) )] -pub(crate) fn prove( +pub fn prove( prover_state: &mut ProverState, code: &C, mt_leaf_hash_params: &::Parameters, diff --git a/src/protocol/phases/proximity.rs b/src/protocol/phases/proximity.rs index 814658e..280df70 100644 --- a/src/protocol/phases/proximity.rs +++ b/src/protocol/phases/proximity.rs @@ -102,7 +102,7 @@ where skip_all, fields(t = t, l2 = l2) )] -pub(crate) fn verify( +pub fn verify( queries: &QueryIndices, rt_0: &MT::InnerDigest, l2_roots: &[MT::InnerDigest], diff --git a/src/protocol/phases/twin_constraint.rs b/src/protocol/phases/twin_constraint.rs index 80ef2b1..d2f652a 100644 --- a/src/protocol/phases/twin_constraint.rs +++ b/src/protocol/phases/twin_constraint.rs @@ -224,7 +224,7 @@ where /// `c_d = T − 2·c_0 − c_1 − … − c_{d−1}`. Matches the encoding in /// `src/protocol/transcript/verifier.rs::derive_randomness`. #[tracing::instrument(name = "twin_constraint.verify", skip_all)] -pub(crate) fn verify_claim( +pub fn verify_claim( sigma_1: F, coeffs_per_round: Vec>, gamma: &[F], diff --git a/src/types.rs b/src/types.rs index dbb8de7..4db018d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -104,7 +104,7 @@ pub struct WARPProof { /// Intermediate output of the PESAT reduction phase. /// /// This data flows from Phase 2 (PESAT Reduction) into Phase 3 (Constrained Code Accumulation). -pub(crate) struct PesatOutput { +pub struct PesatOutput { /// Encoded codewords from fresh witnesses. pub codewords: Vec>, /// Merkle tree over the interleaved codeword leaves. diff --git a/tests/integration_warp.rs b/tests/integration_warp.rs new file mode 100644 index 0000000..98c2cb5 --- /dev/null +++ b/tests/integration_warp.rs @@ -0,0 +1,350 @@ +//! End-to-end integration tests for the full WARP prove / verify / decide +//! cycle. Previously lived in `src/lib.rs` as an inline `#[cfg(test)]` +//! module; moved here so `src/lib.rs` stays focused on the orchestrator. +//! +//! Two suites: +//! +//! - `warp_test` on BLS12-381 (≈254-bit multi-limb) +//! - `warp_test_goldilocks` on Goldilocks (64-bit SmallFp) +//! +//! The suites are deliberately duplicated rather than generic-over-`F`: +//! their associated-type bounds (Merkle config, poseidon config, etc.) +//! differ enough that a single generic function would need a long +//! `where` clause for marginal DRY gain. + +use std::marker::PhantomData; + +use ark_bls12_381::Fr as BLS12_381; +use ark_codes::{ + reed_solomon::{config::ReedSolomonConfig, ReedSolomon}, + traits::LinearCode, +}; +use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; +use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; +use ark_ff::UniformRand; +use ark_serialize::{CanonicalSerialize, Compress}; +use ark_std::rand::thread_rng; + +use warp::config::WARPConfig; +use warp::relations::{ + r1cs::{ + hashchain::{compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness}, + R1CS, + }, + BundledPESAT, Relation, ToPolySystem, +}; +use warp::serialize::{AccInstanceSerializer, AccWitnessSerializer, ProofSerializer}; +use warp::traits::AccumulationScheme; +use warp::types::{AccumulatorInstance, AccumulatorWitness}; +use warp::utils::poseidon; +use warp::WARP; + +#[test] +fn warp_test() { + let l1 = 4; + let s = 8; + let t = 7; + let hash_chain_size = 10; + let mut rng = thread_rng(); + let poseidon_config = poseidon::initialize_poseidon_config::(); + let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + poseidon_config.clone(), + hash_chain_size, + )) + .unwrap(); + let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code = ReedSolomon::new(code_config.clone()); + + let instances_witnesses: (Vec>, Vec>) = (0..l1) + .map(|_| { + let preimage = vec![BLS12_381::rand(&mut rng)]; + let instance = HashChainInstance { + digest: compute_hash_chain::>( + &poseidon_config, + &preimage, + hash_chain_size, + ), + }; + let witness = HashChainWitness { + preimage, + _crhs_scheme: PhantomData::>, + }; + let relation = HashChainRelation::, CRHGadget<_>>::new( + instance, + witness, + (poseidon_config.clone(), hash_chain_size), + ); + (relation.x, relation.w) + }) + .unzip(); + + let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + poseidon_config.clone(), + hash_chain_size, + )) + .unwrap(); + + let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); + let hash_chain_warp = + WARP::, _, Blake3MerkleConfig>::new( + warp_config.clone(), + code.clone(), + r1cs.clone(), + (), + (), + ); + + let (mut acc_roots, mut acc_alphas, mut acc_mus, mut acc_taus, mut acc_xs, mut acc_eta) = + (vec![], vec![], vec![], vec![], vec![], vec![]); + let (mut acc_tds, mut acc_f, mut acc_ws) = (vec![], vec![], vec![]); + + for _ in 0..l1 { + let domainsep = spongefish::domain_separator!("test::warp"); + let mut prover_state = domainsep.instance(&0u32).std_prover(); + let ((acc_x, acc_w), _pf) = hash_chain_warp + .prove( + (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + &mut prover_state, + instances_witnesses.1.clone(), + instances_witnesses.0.clone(), + AccumulatorInstance::empty(), + AccumulatorWitness::empty(), + ) + .unwrap(); + acc_roots.push(acc_x.rt[0].clone()); + acc_alphas.push(acc_x.alpha[0].clone()); + acc_mus.push(acc_x.mu[0]); + acc_taus.push(acc_x.beta.0[0].clone()); + acc_xs.push(acc_x.beta.1[0].clone()); + acc_eta.push(acc_x.eta[0]); + + acc_tds.push(acc_w.td[0].clone()); + acc_f.push(acc_w.f[0].clone()); + acc_ws.push(acc_w.w[0].clone()); + } + + let domainsep = spongefish::domain_separator!("test::warp"); + let warp_config = + WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); + + let hash_chain_warp = + WARP::, _, Blake3MerkleConfig>::new( + warp_config.clone(), + code.clone(), + r1cs.clone(), + (), + (), + ); + + let mut prover_state = domainsep.instance(&0u32).std_prover(); + let ((acc_x, acc_w), pf) = hash_chain_warp + .prove( + (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + &mut prover_state, + instances_witnesses.1, + instances_witnesses.0, + AccumulatorInstance { + rt: acc_roots, + alpha: acc_alphas, + mu: acc_mus, + beta: (acc_taus, acc_xs), + eta: acc_eta, + }, + AccumulatorWitness { + td: acc_tds, + f: acc_f, + w: acc_ws, + }, + ) + .unwrap(); + + let narg_str = prover_state.narg_string().to_vec(); + let domainsep_v = spongefish::domain_separator!("test::warp"); + let mut verifier_state = domainsep_v.instance(&0u32).std_verifier(&narg_str); + hash_chain_warp + .verify( + (r1cs.m, r1cs.n, r1cs.k), + &mut verifier_state, + acc_x.clone(), + pf.clone(), + ) + .unwrap(); + hash_chain_warp + .decide(acc_w.clone(), acc_x.clone()) + .unwrap(); + + let acc_x_to_serde = AccInstanceSerializer::<_, Blake3MerkleConfig>::new(acc_x); + let acc_w_to_serde = AccWitnessSerializer::<_, Blake3MerkleConfig>::new(acc_w); + let proof_to_serde = ProofSerializer::new(pf); + + println!( + "acc_x size: {}", + acc_x_to_serde.serialized_size(Compress::Yes) + ); + println!( + "acc_w size: {}", + acc_w_to_serde.serialized_size(Compress::Yes) + ); + println!( + "proof size: {}", + proof_to_serde.serialized_size(Compress::Yes) + ); + println!("narg_str size: {}", narg_str.len()); +} + +#[test] +fn warp_test_goldilocks() { + use warp::utils::fields::Goldilocks; + + let l1 = 4; + let s = 8; + let t = 7; + let hash_chain_size = 10; + let mut rng = thread_rng(); + let poseidon_config = poseidon::initialize_poseidon_config::(); + let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + poseidon_config.clone(), + hash_chain_size, + )) + .unwrap(); + let code_config = + ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code = ReedSolomon::new(code_config); + + let instances_witnesses: (Vec>, Vec>) = (0..l1) + .map(|_| { + let preimage = vec![Goldilocks::rand(&mut rng)]; + let instance = HashChainInstance { + digest: compute_hash_chain::>( + &poseidon_config, + &preimage, + hash_chain_size, + ), + }; + let witness = HashChainWitness { + preimage, + _crhs_scheme: PhantomData::>, + }; + let relation = HashChainRelation::, CRHGadget<_>>::new( + instance, + witness, + (poseidon_config.clone(), hash_chain_size), + ); + (relation.x, relation.w) + }) + .unzip(); + + let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + poseidon_config.clone(), + hash_chain_size, + )) + .unwrap(); + + let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); + let hash_chain_warp = + WARP::, _, Blake3MerkleConfig>::new( + warp_config.clone(), + code.clone(), + r1cs.clone(), + (), + (), + ); + + let (mut acc_roots, mut acc_alphas, mut acc_mus, mut acc_taus, mut acc_xs, mut acc_eta) = + (vec![], vec![], vec![], vec![], vec![], vec![]); + let (mut acc_tds, mut acc_f, mut acc_ws) = (vec![], vec![], vec![]); + + for _ in 0..l1 { + let domainsep = spongefish::domain_separator!("test::warp"); + let mut prover_state = domainsep.instance(&0u32).std_prover(); + let ((acc_x, acc_w), _pf) = hash_chain_warp + .prove( + (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + &mut prover_state, + instances_witnesses.1.clone(), + instances_witnesses.0.clone(), + AccumulatorInstance::empty(), + AccumulatorWitness::empty(), + ) + .unwrap(); + acc_roots.push(acc_x.rt[0].clone()); + acc_alphas.push(acc_x.alpha[0].clone()); + acc_mus.push(acc_x.mu[0]); + acc_taus.push(acc_x.beta.0[0].clone()); + acc_xs.push(acc_x.beta.1[0].clone()); + acc_eta.push(acc_x.eta[0]); + + acc_tds.push(acc_w.td[0].clone()); + acc_f.push(acc_w.f[0].clone()); + acc_ws.push(acc_w.w[0].clone()); + } + + let domainsep = spongefish::domain_separator!("test::warp"); + // Use 8 (2*l1) for the total accumulation size to test multi-instance accumulation + let warp_config = + WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); + + let hash_chain_warp = + WARP::, _, Blake3MerkleConfig>::new( + warp_config.clone(), + code.clone(), + r1cs.clone(), + (), + (), + ); + + let mut prover_state = domainsep.instance(&0u32).std_prover(); + let ((acc_x, acc_w), pf) = hash_chain_warp + .prove( + (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + &mut prover_state, + instances_witnesses.1, + instances_witnesses.0, + AccumulatorInstance { + rt: acc_roots, + alpha: acc_alphas, + mu: acc_mus, + beta: (acc_taus, acc_xs), + eta: acc_eta, + }, + AccumulatorWitness { + td: acc_tds, + f: acc_f, + w: acc_ws, + }, + ) + .unwrap(); + + let narg_str = prover_state.narg_string().to_vec(); + let domainsep_v = spongefish::domain_separator!("test::warp"); + let mut verifier_state = domainsep_v.instance(&0u32).std_verifier(&narg_str); + hash_chain_warp + .verify( + (r1cs.m, r1cs.n, r1cs.k), + &mut verifier_state, + acc_x.clone(), + pf.clone(), + ) + .unwrap(); + hash_chain_warp + .decide(acc_w.clone(), acc_x.clone()) + .unwrap(); + + let acc_x_to_serde = AccInstanceSerializer::<_, Blake3MerkleConfig>::new(acc_x); + let acc_w_to_serde = AccWitnessSerializer::<_, Blake3MerkleConfig>::new(acc_w); + let proof_to_serde = ProofSerializer::new(pf); + + println!( + "Goldilocks acc_x size: {}", + acc_x_to_serde.serialized_size(Compress::Yes) + ); + println!( + "Goldilocks acc_w size: {}", + acc_w_to_serde.serialized_size(Compress::Yes) + ); + println!( + "Goldilocks proof size: {}", + proof_to_serde.serialized_size(Compress::Yes) + ); + println!("Goldilocks narg_str size: {}", narg_str.len()); +} From 8b6e66a1ebb1e5ed9124a3c198cddd0c67d36f2e Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Sat, 18 Apr 2026 15:02:05 +0200 Subject: [PATCH 15/68] CHANGELOG: document sumcheck communication-reduction work Captures the changelog entries that were uncommitted in the working tree at the start of the Plan 0 / O / B / P / T refactor session. Describes the already-landed sumcheck work in the preceding five commits (a66f122 .. 312e220): inner-product sumcheck 2-coefficient round messages, RoundPolyEvaluator adoption, twin-constraint coefficient count reduction, and the ark-ff rev pin. --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23dbfa2..e9569dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - **Consolidated error types.** Reduced from 6 error enums to 4. Folded `WARPSumcheckProverError` into `ProverError`, inlined `WARPSumcheckVerifierError` into `VerifierError`, dropped `WARP` prefix. Moved errors from `utils/errs.rs` to `error.rs`. - **Made `chunk_size` compile-time.** Replaced runtime serialization with a `const fn` computed from `PrimeField::MODULUS_BIT_SIZE`. +### Optimized + +- **Reduced inner product sumcheck communication by 1/3.** Updated to `efficient-sumcheck` 2-coefficient round messages: prover sends `(a, b)` instead of `(s(0), s(1), s(1/2))`. Verifier derives the third coefficient as `c = claim - 2a - b`. Updated verifier transcript to read `[F; 2]` instead of `[F; 3]` per round. +- **Adopted `RoundPolyEvaluator` trait for twin constraint sumcheck.** Replaced closure-based `twin_constraint_round_poly` with `TwinConstraintEvaluator` struct implementing the new `RoundPolyEvaluator` trait from `efficient-sumcheck`. The library now handles pair iteration, parallel summation, and SIMD-accelerated reduce. +- **Reduced twin constraint sumcheck communication.** Prover now sends `d` coefficients per round instead of `d+1`; verifier derives the leading coefficient from the sumcheck constraint. Updated `derive_randomness` to read `1 + max(log_n+1, log_m+2)` instead of `2 + max(log_n+1, log_m+2)`. +- **Twin constraint sumcheck 64% faster.** Combined effect of `RoundPolyEvaluator` refactor, library-side optimizations (zero-allocation `poly_ops`, optimized `protogalaxy::fold`), and SIMD-accelerated pairwise reduce. End-to-end prover time improved ~23%. +- **Pinned `ark-ff`/`ark-poly`/`ark-serialize` to rev `285dac2`.** Resolves spongefish BigInt mismatch with upstream `ark-ff` HEAD. + ### Fixed - **BLS test desync.** Fixed `WARPConfig` in the BLS test from `l=4` to `l=8`, matching `l2=4` accumulated instances. The prover absorbed all accumulators unconditionally while the verifier only read `l2` of them, causing a sponge state mismatch. From 28ab1805a3a045d8bd1154b59d4dd49fc9eaa8fe Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Sat, 18 Apr 2026 15:03:08 +0200 Subject: [PATCH 16/68] gitignore: widen from .claude/settings.local.json to .claude/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Covers every personal Claude Code artefact (custom agents, plans, skills, settings.json, settings.local.json) for this repo — not just the local-settings file that was already ignored. Nothing under .claude/ has ever been tracked here, so this is a purely belt-and-suspenders tightening: the next `git add .` can't pick up anything from the directory. --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1857d77..d6cf5e8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ /Cargo.lock .vscode .DS_Store -.claude/settings.local.json \ No newline at end of file +.claude/ \ No newline at end of file From f36d25866bd90c2ad71ae4fc391ad10c1487d031 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Sun, 19 Apr 2026 16:44:46 +0200 Subject: [PATCH 17/68] integrate new effsc --- Cargo.toml | 2 +- benches/utils/domainsep.rs | 2 +- examples/profile_phases.rs | 212 ++++++++++++++++++++ src/bin/warp-params.rs | 26 +-- src/error.rs | 20 ++ src/lib.rs | 256 ++++++++++++++----------- src/params/mod.rs | 6 +- src/params/types.rs | 9 +- src/protocol/phases/batching.rs | 90 ++++++--- src/protocol/phases/proximity.rs | 3 +- src/protocol/phases/twin_constraint.rs | 72 +++---- src/protocol/transcript/mod.rs | 26 +++ src/protocol/transcript/verifier.rs | 72 +++---- src/relations/r1cs/mod.rs | 7 +- src/utils/poly.rs | 28 +-- tests/integration_warp.rs | 51 ++--- tests/profile_json.rs | 37 ++-- tests/verifier_negative.rs | 20 +- 18 files changed, 616 insertions(+), 323 deletions(-) create mode 100644 examples/profile_phases.rs diff --git a/Cargo.toml b/Cargo.toml index 5f0a9bb..5b4084b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ serde_json = "1.0" spongefish = { git = "https://github.com/z-tech/spongefish.git", branch = "smallfp-support", features = [ "ark-ff", ] } -efficient-sumcheck = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git", branch = "z-tech/simd_goldilocks_experimental" } +effsc = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git", branch = "z-tech/rewrite-v2" } thiserror = "2.0.16" tracing = { version = "0.1", default-features = false, features = ["std", "attributes"] } tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter", "json", "registry"], optional = true } diff --git a/benches/utils/domainsep.rs b/benches/utils/domainsep.rs index 9c537ab..5ef1355 100644 --- a/benches/utils/domainsep.rs +++ b/benches/utils/domainsep.rs @@ -2,5 +2,5 @@ use spongefish::ProverState; pub fn init_prover_state() -> ProverState { let domainsep = spongefish::domain_separator!("warp::rs"); - domainsep.instance(&0u32).std_prover() + domainsep.without_session().instance(&0u32).std_prover() } diff --git a/examples/profile_phases.rs b/examples/profile_phases.rs new file mode 100644 index 0000000..3f4291b --- /dev/null +++ b/examples/profile_phases.rs @@ -0,0 +1,212 @@ +//! Per-phase wall-time profile of a Goldilocks WARP prove run — used to +//! report the micro-profile table after the rewrite-v2 effsc integration. +//! See the original shape in the PR #22 body. +//! +//! Run: +//! ```text +//! cargo run --release --features profile --example profile_phases +//! ``` +//! +//! The `profile` feature installs the JSON layer from `warp::profile::init_json`. +//! This binary captures the emitted records, aggregates wall_ns per phase +//! over a handful of prove invocations, and prints a markdown table that +//! mirrors the PR #22 breakdown (rs_encode, pesat_merkle_tree, +//! twin_constraint_sumcheck, etc.). +//! +//! Best run with `RAYON_NUM_THREADS` pinned and all other workloads off. + +#[cfg(not(feature = "profile"))] +fn main() { + eprintln!( + "profile_phases: build with --features profile (and preferably --release).\n\ + example: cargo run --release --features profile --example profile_phases" + ); +} + +#[cfg(feature = "profile")] +fn main() { + inner::run(); +} + +#[cfg(feature = "profile")] +mod inner { + use std::io::{self, Write}; + use std::marker::PhantomData; + use std::sync::{Arc, Mutex}; + + use ark_codes::reed_solomon::config::ReedSolomonConfig; + use ark_codes::reed_solomon::ReedSolomon; + use ark_codes::traits::LinearCode; + use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; + use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; + use ark_std::rand::thread_rng; + use ark_std::UniformRand; + + use warp::config::WARPConfig; + use warp::relations::{ + r1cs::{ + hashchain::{ + compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness, + }, + R1CS, + }, + BundledPESAT, Relation, ToPolySystem, + }; + use warp::traits::AccumulationScheme; + use warp::types::{AccumulatorInstance, AccumulatorWitness}; + use warp::utils::fields::Goldilocks; + use warp::utils::poseidon; + use warp::WARP; + + #[derive(Clone)] + struct SharedBuf(Arc>>); + + impl Write for SharedBuf { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.lock().unwrap().extend_from_slice(buf); + Ok(buf.len()) + } + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + pub fn run() { + let iters = std::env::var("WARP_PROFILE_ITERS") + .ok() + .and_then(|s| s.parse::().ok()) + .unwrap_or(5); + let hash_chain_size = std::env::var("WARP_PROFILE_HCSIZE") + .ok() + .and_then(|s| s.parse::().ok()) + .unwrap_or(10); + let warmup = 2usize; + + let sink = SharedBuf(Arc::new(Mutex::new(Vec::new()))); + let installed = warp::profile::init_json(sink.clone()); + assert!(installed, "failed to install JSON profile subscriber"); + + let l1 = 4; + let s = 8; + let t = 7; + let mut rng = thread_rng(); + let poseidon_config = poseidon::initialize_poseidon_config::(); + let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + poseidon_config.clone(), + hash_chain_size, + )) + .unwrap(); + let code_config = + ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code = ReedSolomon::new(code_config); + let n = code.code_len(); + + let (instances, witnesses): (Vec<_>, Vec<_>) = (0..l1) + .map(|_| { + let preimage = vec![Goldilocks::rand(&mut rng)]; + let instance = HashChainInstance { + digest: compute_hash_chain::>( + &poseidon_config, + &preimage, + hash_chain_size, + ), + }; + let witness = HashChainWitness { + preimage, + _crhs_scheme: PhantomData::>, + }; + let relation = HashChainRelation::, CRHGadget<_>>::new( + instance, + witness, + (poseidon_config.clone(), hash_chain_size), + ); + (relation.x, relation.w) + }) + .unzip(); + + let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); + let prover = WARP::, _, Blake3MerkleConfig>::new( + warp_config, + code.clone(), + r1cs.clone(), + (), + (), + ); + + println!( + "=== warp profile (Goldilocks hashchain) — n={n}, l1={l1}, s={s}, t={t}, hashchain_size={hash_chain_size}, iters={iters} (+ {warmup} warmup)" + ); + + for i in 0..(warmup + iters) { + let domainsep = spongefish::domain_separator!("profile_phases"); + let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); + if i == warmup { + sink.0.lock().unwrap().clear(); + } + prover + .prove( + (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + &mut prover_state, + witnesses.clone(), + instances.clone(), + AccumulatorInstance::empty(), + AccumulatorWitness::empty(), + ) + .unwrap(); + } + + let bytes = sink.0.lock().unwrap().clone(); + let text = String::from_utf8(bytes).expect("JSON output is UTF-8"); + + let rows: &[(&str, &str)] = &[ + ("pesat.encode", "rs_encode (FFT)"), + ("pesat.merkle_commit", "pesat_merkle_tree"), + ("twin_constraint.sumcheck", "twin_constraint_sumcheck"), + ("warp.commit_new_oracle", "merkle_commit"), + ("batching.eq_evals", "eq_poly_evals + ood_evals_vec"), + ("batching.sumcheck", "batching_sumcheck (inner product)"), + ("warp.prove", "end-to-end prover"), + ]; + + let mut series: std::collections::BTreeMap<&str, Vec> = + std::collections::BTreeMap::new(); + for line in text.lines() { + for (phase, _) in rows.iter() { + let needle = format!("\"phase\":\"{phase}\""); + if line.contains(&needle) { + if let Some(w) = extract_u64(line, "\"wall_ns\":") { + series.entry(*phase).or_default().push(w); + } + break; + } + } + } + + println!(); + println!("| Phase | n | mean (ms) | min (ms) | samples |"); + println!("|-------|---|-----------|----------|---------|"); + for (phase, label) in rows { + let vs = series.remove(*phase).unwrap_or_default(); + if vs.is_empty() { + println!("| {label} | {n} | n/a | n/a | 0 |"); + continue; + } + let sum_ns: u128 = vs.iter().map(|&v| v as u128).sum(); + let mean_ms = (sum_ns as f64 / vs.len() as f64) / 1e6; + let min_ms = *vs.iter().min().unwrap() as f64 / 1e6; + println!( + "| {label} | {n} | {mean_ms:.3} | {min_ms:.3} | {} |", + vs.len() + ); + } + } + + fn extract_u64(line: &str, key: &str) -> Option { + let i = line.find(key)? + key.len(); + let rest = &line[i..]; + let end = rest + .find(|c: char| !c.is_ascii_digit()) + .unwrap_or(rest.len()); + rest[..end].parse().ok() + } +} diff --git a/src/bin/warp-params.rs b/src/bin/warp-params.rs index 71d3662..2015992 100644 --- a/src/bin/warp-params.rs +++ b/src/bin/warp-params.rs @@ -15,9 +15,7 @@ use std::process::ExitCode; -use warp::params::{ - lookup, select, validate, ParamError, Params, Regime, SecurityLevel, PRESETS, -}; +use warp::params::{lookup, select, validate, ParamError, Params, Regime, SecurityLevel, PRESETS}; fn main() -> ExitCode { let args: Vec = std::env::args().skip(1).collect(); @@ -58,13 +56,14 @@ fn cmd_select(args: &[String]) -> ExitCode { return ExitCode::from(2); } }; - let (lambda, rate, field_bits, regime) = match (flags.lambda, flags.rate, flags.field_bits, flags.regime) { - (Some(l), Some(r), Some(fb), Some(rg)) => (SecurityLevel(l), r, fb, rg), - _ => { - eprintln!("warp-params select: need --lambda, --rate, --field-bits, --regime"); - return ExitCode::from(2); - } - }; + let (lambda, rate, field_bits, regime) = + match (flags.lambda, flags.rate, flags.field_bits, flags.regime) { + (Some(l), Some(r), Some(fb), Some(rg)) => (SecurityLevel(l), r, fb, rg), + _ => { + eprintln!("warp-params select: need --lambda, --rate, --field-bits, --regime"); + return ExitCode::from(2); + } + }; match select(lambda, field_bits, rate.as_f64(), regime) { Ok(p) => { // Prefer a preset if we have an exact-rational match on file — @@ -120,7 +119,9 @@ fn cmd_validate(args: &[String]) -> ExitCode { (s, t, SecurityLevel(l), r, fb, rg) } _ => { - eprintln!("warp-params validate: need --s, --t, --lambda, --rate, --field-bits, --regime"); + eprintln!( + "warp-params validate: need --s, --t, --lambda, --rate, --field-bits, --regime" + ); return ExitCode::from(2); } }; @@ -217,7 +218,8 @@ fn parse_flags(args: &[String]) -> Result { } fn parse_u32(s: &str) -> Result { - s.parse().map_err(|e| format!("expected u32, got `{s}`: {e}")) + s.parse() + .map_err(|e| format!("expected u32, got `{s}`: {e}")) } fn parse_rate(s: &str) -> Result { diff --git a/src/error.rs b/src/error.rs index 3f016ab..681124c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -73,6 +73,26 @@ impl From for VerifierError { } } +impl From for VerifierError { + fn from(err: effsc::proof::SumcheckError) -> Self { + use effsc::proof::SumcheckError; + match err { + // Round consistency or degree checks failed inside the library. + SumcheckError::ConsistencyCheck { .. } | SumcheckError::DegreeMismatch { .. } => { + Self::SumcheckRound + } + // The caller-supplied oracle_check (the final-claim equality) + // rejected — surfaces as warp's `Target` variant for backwards + // compatibility with the existing negative tests. + SumcheckError::FinalEvaluation => Self::Target, + // Ran out of transcript or malformed bytes. + SumcheckError::TranscriptError { .. } => Self::SpongeFish, + // Not reachable: warp passes `noop_hook_verify`. + SumcheckError::HookError { .. } => Self::SumcheckRound, + } + } +} + #[derive(Error, Debug)] pub enum DeciderError { #[error("Invalid merkle root")] diff --git a/src/lib.rs b/src/lib.rs index 3564783..3f655c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,13 +11,14 @@ use ark_ff::{Field, PrimeField}; use ark_poly::{DenseMultilinearExtension, Polynomial}; use ark_std::log2; use config::WARPConfig; -use efficient_sumcheck::{ - hypercube::{compute_hypercube_eq_evals, Hypercube}, - order_strategy::AscendingOrder, +use effsc::{ + hypercube::{compute_hypercube_eq_evals, Ascending}, + verifier::sumcheck_verify, }; use protocol::query::QueryIndices; use protocol::transcript::{ - absorb_instances, derive_randomness, parse_statement, DerivedRandomness, + absorb_instances, derive_between_sumchecks, derive_pre_twin_constraint, parse_statement, + BetweenSumchecks, EffscVerifierTranscript, PreTwinConstraint, }; use relations::{r1cs::R1CSConstraints, BundledPESAT}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; @@ -283,18 +284,15 @@ impl< let (l1, l) = (self.params.config.l1, self.params.config.l); let l2 = l - l1; - //////////////////////// - // 1. Parsing phase - //////////////////////// - // a. verification key #[allow(non_snake_case)] let (M, N, k) = (vk.0, vk.1, vk.2); let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); - let n = self.params.code.code_len(); let log_n = log2(n) as usize; + let r = 1 + self.params.config.s + self.params.config.t; + let log_r = log2(r) as usize; - // f. absorb parameters + // 1. Parse statement (fresh instances + accumulator). let ( l1_xs, AccumulatorInstance { @@ -306,153 +304,180 @@ impl< }, ) = parse_statement::(verifier_state, l1, l2, N - k, log_n, log_m)?; - //////////////////////// - // 2. Derive randomness - //////////////////////// - let DerivedRandomness { + // 2. Read PESAT phase: rt_0 + l1_mus, squeeze l1_taus, ω, τ. + let PreTwinConstraint { rt_0, l1_mus, l1_taus, omega, tau, - gamma_sumcheck, - coeffs_twinc_sumcheck, + } = derive_pre_twin_constraint::(verifier_state, l1, log_l, log_m)?; + + // 3. Twin-constraint sumcheck claim σ₁ = Σ_i τ_eq(i)·(μ_i + ω·η_i). + let tau_eq_evals = compute_hypercube_eq_evals(log_l, &tau); + let etas_l2_first = concat_slices(&l2_etas, &vec![F::zero(); l1]); + let sigma_1 = tau_eq_evals + .into_iter() + .zip( + l2_mus + .iter() + .copied() + .chain(l1_mus.iter().copied()) + .zip(etas_l2_first), + ) + .fold(F::zero(), |acc, (eq_tau, (mu, eta))| { + acc + eq_tau * (mu + omega * eta) + }); + + // 4. Run the twin-constraint sumcheck via effsc. The real oracle + // check is `final_claim == eq(τ, γ) · (ν₀ + ω·η)`, but ν₀ and η + // arrive on the transcript *after* the sumcheck rounds — so we + // capture `final_claim` in the closure and finish the check + // below after reading them. `sumcheck_verify` still enforces all + // per-round consistency (`q(0)+q(1)==claim`) automatically. + let tc_degree = 1 + (log_n + 1).max(log_m + 2); + let mut tc_final_claim = F::zero(); + let gamma_sumcheck = { + let mut wrap = EffscVerifierTranscript(verifier_state); + sumcheck_verify( + sigma_1, + tc_degree, + log_l, + &mut wrap, + |_, _| Ok(()), + |final_claim, _challenges| { + tc_final_claim = final_claim; + Ok(()) + }, + )? + }; + + // 5. Read between-sumchecks state: td, η, ν₀, OOD, shift-query + // byte challenges, ξ. + let BetweenSumchecks { td: _, eta, - mut nus, + nus: mut nus_from_transcript, ood_samples, bytes_shift_queries, xi, - alpha_sumcheck, - sums_batching_sumcheck, - } = derive_randomness::( + } = derive_between_sumchecks::( verifier_state, - l1, log_n, - log_l, self.params.config.s, self.params.config.t, - log_m, )?; - let r = 1 + self.params.config.s + self.params.config.t; - let log_r = log2(r) as usize; + // 6. Deferred twin-constraint oracle check. + (eq_poly_non_binary(&tau, &gamma_sumcheck) * (nus_from_transcript[0] + omega * eta) + == tc_final_claim) + .ok_or_err(VerifierError::Target)?; - //////////////////////// - // 3. Derive values - //////////////////////// - // b. - let alpha_vecs = concat_slices(&l2_alphas, &vec![vec![F::zero(); log_n]; l1]); + // 7. Proximity first: both the batching σ₂ and the batching oracle + // check consume `proof.shift_query_answers`, so we must reject + // malformed / tampered openings *before* feeding them into the + // sumcheck. Running proximity here preserves the existing + // negative-test error distinctions (`ShiftQuery`, `ShiftQueryIndex`, + // `NumShiftQueries`, `NumL2Instances`). + let queries: QueryIndices = + QueryIndices::from_squeezed_bytes(&bytes_shift_queries, log_n, self.params.config.t); - let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma_sumcheck); + proximity::verify::( + &queries, + &rt_0, + &l2_roots, + &proof.auth_0, + &proof.auth_j, + &proof.shift_query_answers, + &self.params.mt_leaf_hash_params, + &self.params.mt_two_to_one_hash_params, + l2, + self.params.config.t, + )?; + // 8. Derive everything needed to state the batching claim. + let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma_sumcheck); + let alpha_vecs = concat_slices(&l2_alphas, &vec![vec![F::zero(); log_n]; l1]); let zeta_0 = scale_and_sum(&alpha_vecs, &gamma_eq_evals); - // compute \eta_{s + k} + // ν_{s+k}: inner product of the (now proximity-verified) shift-query + // answers with the γ-equality table. let mut nu_s_t = vec![F::default(); self.params.config.t]; for (i, v_jk) in proof.shift_query_answers.iter().enumerate() { - let res = v_jk + nu_s_t[i] = v_jk .iter() .zip(&gamma_eq_evals) .fold(F::zero(), |acc, (v, eq)| acc + *eq * *v); - nu_s_t[i] = res; } - - nus.extend(nu_s_t); - - // d. set \sigma^{(1)} and \sigma^{(2)} - // compute eq(\tau, i) and eq(\xi, i) - let tau_eq_evals = compute_hypercube_eq_evals(log_l, &tau); - - let etas = concat_slices(&l2_etas, &vec![F::zero(); l1]); - - let sigma_1 = tau_eq_evals - .into_iter() - .zip(l2_mus.into_iter().chain(l1_mus.to_vec()).zip(etas)) - .fold(F::zero(), |acc, (eq_tau, (mu, eta))| { - acc + eq_tau * (mu + omega * eta) - }); + nus_from_transcript.extend(nu_s_t); + let nus = nus_from_transcript; let xi_eq_evals = compute_hypercube_eq_evals(log_r, &xi); - let sigma_2 = xi_eq_evals .iter() .zip(&nus) .fold(F::zero(), |acc, (xi_eq, nu)| acc + *xi_eq * nu); - //////////////////////// - // 4. Decision phase - //////////////////////// - // a. new code evaluation point - (acc_instance.alpha[0] == alpha_sumcheck).ok_or_err(VerifierError::CodeEvaluationPoint)?; + // 8. Run the batching (inner-product) sumcheck via effsc. The + // oracle check `final_claim == μ · Σ_i eq(ζ_i, α)·ξ_eq(i)` is + // self-contained and runs inside the closure — a mismatch maps + // to `VerifierError::Target`. We still need the reduced α to + // compare against `acc_instance.alpha[0]` below, so the closure + // returns Ok via a side-channel capture of the zeta-eq sum. + // + // α arrives in MSB (sumcheck round) order; the prover stored + // the LSB-reversed form in `acc_instance.alpha[0]`, so we + // reverse once for MLE/eq consistency with arkworks. + let acc_mu = acc_instance.mu[0]; + let alpha_sumcheck_msb = { + let mut wrap = EffscVerifierTranscript(verifier_state); + sumcheck_verify( + sigma_2, + 2, + log_n, + &mut wrap, + |_, _| Ok(()), + |final_claim, alpha_msb| { + let alpha_lsb: Vec = alpha_msb.iter().rev().copied().collect(); + let mut zeta_eqs = Vec::with_capacity(r); + zeta_eqs.push(eq_poly_non_binary(&zeta_0, &alpha_lsb)); + for chunk in ood_samples.chunks(log_n) { + zeta_eqs.push(eq_poly_non_binary(chunk, &alpha_lsb)); + } + for pt in &queries.evaluation_points { + zeta_eqs.push(eq_poly_non_binary(pt, &alpha_lsb)); + } + debug_assert_eq!(zeta_eqs.len(), r); + let expected = acc_mu + * zeta_eqs + .into_iter() + .zip(&xi_eq_evals) + .fold(F::zero(), |acc, (a, b)| acc + a * *b); + if expected == final_claim { + Ok(()) + } else { + Err(effsc::proof::SumcheckError::FinalEvaluation) + } + }, + )? + }; + + // 10. Accumulator consistency checks for the new code / circuit + // evaluation points. + let alpha_sumcheck_lsb: Vec = alpha_sumcheck_msb.iter().rev().copied().collect(); + (acc_instance.alpha[0] == alpha_sumcheck_lsb) + .ok_or_err(VerifierError::CodeEvaluationPoint)?; - // b. new circuit evaluation point let betas = l2_taus .into_iter() .chain(l1_taus) .zip(l2_xs.clone().into_iter().chain(l1_xs)) - .map(|(tau, x)| concat_slices(&tau, &x)) + .map(|(tau_i, x)| concat_slices(&tau_i, &x)) .collect::>>(); let beta = scale_and_sum(&betas, &gamma_eq_evals); let expected_beta = concat_slices(&acc_instance.beta.0[0], &acc_instance.beta.1[0]); (expected_beta == beta).ok_or_err(VerifierError::CircuitEvaluationPoint)?; - // c. proximity: check auth paths + leaf-position consistency. - let queries: QueryIndices = - QueryIndices::from_squeezed_bytes(&bytes_shift_queries, log_n, self.params.config.t); - - proximity::verify::( - &queries, - &rt_0, - &l2_roots, - &proof.auth_0, - &proof.auth_j, - &proof.shift_query_answers, - &self.params.mt_leaf_hash_params, - &self.params.mt_two_to_one_hash_params, - l2, - self.params.config.t, - )?; - - // d. sumcheck decisions: reduce each transcript's round messages to - // a single target, then compare to the expected claim in (e). - (coeffs_twinc_sumcheck.len() == log_l).ok_or_err(VerifierError::NumSumcheckRounds)?; - let target_1 = twin_constraint::verify_claim(sigma_1, coeffs_twinc_sumcheck, &gamma_sumcheck); - - (sums_batching_sumcheck.len() == log_n).ok_or_err(VerifierError::NumSumcheckRounds)?; - let target_2 = batching::verify_claim(sigma_2, sums_batching_sumcheck, &alpha_sumcheck); - - // e. new target decision - // build eq^{\star}(\alpha) - (eq_poly_non_binary(&tau, &gamma_sumcheck) * (nus[0] + omega * eta) == target_1) - .ok_or_err(VerifierError::Target)?; - - let mut zeta_eqs = vec![eq_poly_non_binary(&zeta_0, &alpha_sumcheck)]; - - zeta_eqs.extend( - ood_samples - .chunks(log_n) - .map(|zeta| eq_poly_non_binary(zeta, &alpha_sumcheck)) - .collect::>(), - ); - zeta_eqs.extend( - queries - .evaluation_points - .iter() - .map(|zeta| eq_poly_non_binary(zeta, &alpha_sumcheck)) - .collect::>(), - ); - (zeta_eqs.len() == r).ok_or_err(VerifierError::NumShiftQueries)?; - - // mul by \mu and compare to target_2 - (acc_instance.mu[0] - * zeta_eqs - .into_iter() - .zip(xi_eq_evals) - .fold(F::zero(), |acc, (a, b)| acc + a * b) - == target_2) - .ok_or_err(VerifierError::Target)?; - Ok(()) } @@ -478,8 +503,8 @@ impl< let tau = &acc_instance.beta.0[0]; - let tau_zero_evader = Hypercube::::new(tau.len()) - .map(|(index, _point)| eq_poly(tau, index)) + let tau_zero_evader = Ascending::new(tau.len()) + .map(|p| eq_poly(tau, p.index)) .collect::>(); let mut z = acc_instance.beta.1[0].clone(); @@ -497,4 +522,3 @@ impl< Ok(()) } } - diff --git a/src/params/mod.rs b/src/params/mod.rs index fe6fb0d..401d433 100644 --- a/src/params/mod.rs +++ b/src/params/mod.rs @@ -123,10 +123,10 @@ mod tests { #[test] fn presets_match_select_output() { for preset in PRESETS { - let recomputed = - select(preset.lambda, 254, preset.code_rate(), preset.regime).unwrap(); + let recomputed = select(preset.lambda, 254, preset.code_rate(), preset.regime).unwrap(); assert_eq!( - recomputed, preset.params, + recomputed, + preset.params, "preset drift: {:?} rate={:?} regime={:?}", preset.lambda, preset.code_rate(), diff --git a/src/params/types.rs b/src/params/types.rs index c85e833..c2e73c8 100644 --- a/src/params/types.rs +++ b/src/params/types.rs @@ -65,9 +65,7 @@ pub struct SoundnessBound { impl SoundnessBound { /// `true` iff every component check passes at the caller's target. pub fn meets(&self, target: SecurityLevel) -> bool { - self.field_admissible - && self.ood_admissible - && self.proximity_bits >= target.bits() as f64 + self.field_admissible && self.ood_admissible && self.proximity_bits >= target.bits() as f64 } } @@ -79,8 +77,5 @@ pub enum ParamError { InvalidRate, /// Field is too small to support the target soundness even with /// infinite queries. - FieldTooSmall { - field_bits: u32, - lambda: u32, - }, + FieldTooSmall { field_bits: u32, lambda: u32 }, } diff --git a/src/protocol/phases/batching.rs b/src/protocol/phases/batching.rs index ab3c44d..3e44be1 100644 --- a/src/protocol/phases/batching.rs +++ b/src/protocol/phases/batching.rs @@ -17,15 +17,58 @@ use ark_ff::{Field, PrimeField}; use ark_std::log2; -use efficient_sumcheck::{ - accumulate_sparse_evaluations, batched_constraint_poly, inner_product_sumcheck, -}; +use effsc::{noop_hook, provers::inner_product::InnerProductProver, runner::sumcheck}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; +use std::collections::HashMap; use crate::count_ops; use crate::protocol::oracle::Oracle; use crate::utils::poly::eq_poly; +/// [CBBZ23] / HyperPlonk sparse-evaluation optimization: for shift-query +/// zetas (indices `1+s..r`), each ζ is a 0/1 vector representing a single +/// hypercube point. We accumulate the corresponding `eq_evals[i]` into a +/// sparse map keyed by that point's index. +fn accumulate_sparse_evaluations( + zetas: Vec<&[F]>, + eq_evals: Vec, + s: usize, + r: usize, +) -> HashMap { + let mut result: HashMap = HashMap::new(); + for i in 1 + s..r { + let index = zetas[i] + .iter() + .enumerate() + .filter_map(|(j, bit)| bit.is_one().then_some(1 << j)) + .sum::(); + *result.entry(index).or_insert_with(F::zero) += eq_evals[i]; + } + result +} + +/// Sum `dense_polys` column-wise and add the sparse contributions into the +/// resulting vector. Used to build the `g` side of the inner-product +/// sumcheck `∑_x f(x)·g(x)`. +fn batched_constraint_poly( + dense_polys: &[Vec], + sparse_polys: &HashMap, +) -> Vec { + if dense_polys.is_empty() { + return Vec::new(); + } + let mut result = vec![F::ZERO; dense_polys[0].len()]; + for row in dense_polys { + for (i, val) in row.iter().enumerate() { + result[i] += *val; + } + } + for (k, v) in sparse_polys.iter() { + result[*k] += *v; + } + result +} + /// Output of the batching sumcheck: the reduced point `α` and the target /// `μ = \hat f(α)`. pub struct BatchingOutput { @@ -80,36 +123,27 @@ where accumulate_sparse_evaluations(zetas_prefix.to_vec(), xi_eq_evals, s, r) }; - // call efficient sumcheck for batched_constraint checks + // Run the inner-product sumcheck. `InnerProductProver` + `runner::sumcheck` + // is the new-style `SumcheckProver` entry point; wire format is three + // evaluations `[q(0), q(1), q(2)]` per round (`effsc::sumcheck_verify` + // reads them on the verifier side). The prover is MSB half-split, so the + // challenge vector arrives in MSB order — reverse once here so downstream + // MLE / eq_poly queries (arkworks' LSB-first convention) line up. let alpha = { let _s = tracing::info_span!("batching.sumcheck").entered(); - let log_n = ark_std::log2(n) as u64; - count_ops!(BatchingRounds, log_n); - inner_product_sumcheck( - &mut oracle.evals().to_vec(), - &mut batched_constraint_poly(&ood_evals_vec, &id_non_0_eval_sums), - prover_state, - ) - .verifier_messages + let log_n_bits = ark_std::log2(n) as u64; + count_ops!(BatchingRounds, log_n_bits); + let mut ip = InnerProductProver::new( + oracle.evals().to_vec(), + batched_constraint_poly(&ood_evals_vec, &id_non_0_eval_sums), + ); + let mut challenges = + sumcheck(&mut ip, log_n_bits as usize, prover_state, noop_hook).challenges; + challenges.reverse(); + challenges }; let mu = oracle.query_at_point(&alpha); BatchingOutput { alpha, mu } } - -/// Reduce the batching (inner-product / multilinear) sumcheck's per-round -/// messages against the verifier challenges `α`. Each round message is -/// `[a, b]` where `h(X) = a·(1-2X) + b·X + (prev_target - b)·X²`; the -/// caller's `a, b` unpacking mirrors `src/protocol/transcript/verifier.rs`. -#[tracing::instrument(name = "batching.verify", skip_all)] -pub fn verify_claim(sigma_2: F, sums_per_round: Vec<[F; 2]>, alpha: &[F]) -> F -where - F: Field, -{ - let mut target = sigma_2; - for ([a, b], x) in sums_per_round.into_iter().zip(alpha) { - target = (target - b) * x.square() + a * (F::one() - x.double()) + b * x; - } - target -} diff --git a/src/protocol/phases/proximity.rs b/src/protocol/phases/proximity.rs index 280df70..d35ad75 100644 --- a/src/protocol/phases/proximity.rs +++ b/src/protocol/phases/proximity.rs @@ -121,8 +121,7 @@ where (shift_query_answers.len() == t).ok_or_err(VerifierError::NumShiftQueries)?; for (i, path) in auth_0.iter().enumerate() { - (path.leaf_index == queries.leaf_positions[i]) - .ok_or_err(VerifierError::ShiftQueryIndex)?; + (path.leaf_index == queries.leaf_positions[i]).ok_or_err(VerifierError::ShiftQueryIndex)?; count_ops!(MerklePathsVerified); let is_valid = path.verify( diff --git a/src/protocol/phases/twin_constraint.rs b/src/protocol/phases/twin_constraint.rs index d2f652a..211fdf2 100644 --- a/src/protocol/phases/twin_constraint.rs +++ b/src/protocol/phases/twin_constraint.rs @@ -18,12 +18,10 @@ //! Each round's round polynomial has the form `h(X) = (f(X) + ω·p(X))·t(X)`. use ark_ff::{Field, PrimeField}; -use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial}; -use efficient_sumcheck::{ - coefficient_sumcheck::{coefficient_sumcheck, RoundPolyEvaluator}, - folding::protogalaxy, - hypercube::Hypercube, - order_strategy::AscendingOrder, +use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; +use effsc::{ + coefficient_sumcheck::RoundPolyEvaluator, folding::protogalaxy, hypercube::Ascending, + noop_hook, provers::coefficient_lsb::CoefficientProverLSB, runner::sumcheck, }; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; @@ -161,8 +159,8 @@ where let tau = prover_state.verifier_messages_vec::(log_l); // b. assemble sumcheck tables - let tau_eq_evals = Hypercube::::new(log_l) - .map(|(index, _point)| eq_poly(&tau, index)) + let tau_eq_evals = Ascending::new(log_l) + .map(|p| eq_poly(&tau, p.index)) .collect::>(); let alpha_vecs = concat_slices(&acc_instance.alpha, &vec![vec![F::zero(); log_n]; l1]); @@ -178,13 +176,13 @@ where let beta_vecs: Vec> = acc_instance.beta.0.into_iter().chain(fresh_taus).collect(); - let mut tablewise = [ + let tablewise = vec![ concat_slices(acc_witness_f, fresh_codewords), // u z_vecs, // z alpha_vecs, // a beta_vecs, // b ]; - let mut pw = [tau_eq_evals]; // tau + let pw = vec![tau_eq_evals]; // tau let degree = 1 + (log_n + 1).max(log_m + 2); let evaluator = TwinConstraintEvaluator { @@ -193,20 +191,25 @@ where degree, }; - // c. run the sumcheck - let sc = { + // c. run the sumcheck. `CoefficientProverLSB` + `runner::sumcheck` is + // the new-style `SumcheckProver` entry point; wire format is `d+1` + // evaluations per round (the verifier uses `effsc::sumcheck_verify` on + // the other side). + let mut cc = CoefficientProverLSB::new(&evaluator, tablewise, pw); + { let _s = tracing::info_span!("twin_constraint.sumcheck").entered(); count_ops!(TwinConstraintRounds, log_l as u64); - coefficient_sumcheck(&evaluator, &mut tablewise, &mut pw, log_l, prover_state) - }; - debug_assert_eq!(sc.verifier_messages.len(), log_l); + let proof = sumcheck(&mut cc, log_l, prover_state, noop_hook); + debug_assert_eq!(proof.challenges.len(), log_l); + } - // d. pop reduced tables — each group has one table left after log_l rounds - let [mut u_red, mut z_red, mut a_red, mut b_red] = tablewise; - let f = u_red.pop().unwrap(); - let z = z_red.pop().unwrap(); - let zeta_0 = a_red.pop().unwrap(); - let beta_tau = b_red.pop().unwrap(); + // d. pull the single remaining row out of each tablewise table. + let reduced = cc.tablewise(); + debug_assert!(reduced.iter().all(|t| t.len() == 1)); + let f = reduced[0][0].clone(); + let z = reduced[1][0].clone(); + let zeta_0 = reduced[2][0].clone(); + let beta_tau = reduced[3][0].clone(); TwinConstraintOutput { f: Oracle::from_evals(f), @@ -215,30 +218,3 @@ where beta_tau, } } - -/// Reduce the twin-constraint sumcheck's per-round coefficient messages -/// against the verifier challenges `γ`. Returns the final reduced target. -/// -/// The prover sends only `d` coefficients per round; the leading coefficient -/// `c_d` is derived from the round claim `T = 2·c_0 + c_1 + … + c_d` so -/// `c_d = T − 2·c_0 − c_1 − … − c_{d−1}`. Matches the encoding in -/// `src/protocol/transcript/verifier.rs::derive_randomness`. -#[tracing::instrument(name = "twin_constraint.verify", skip_all)] -pub fn verify_claim( - sigma_1: F, - coeffs_per_round: Vec>, - gamma: &[F], -) -> F -where - F: Field, -{ - let mut target = sigma_1; - for (mut coeffs, g) in coeffs_per_round.into_iter().zip(gamma) { - let partial_sum: F = coeffs.iter().skip(1).copied().sum(); - let leading = target - coeffs[0].double() - partial_sum; - coeffs.push(leading); - let h = DensePolynomial::from_coefficients_vec(coeffs); - target = h.evaluate(g); - } - target -} diff --git a/src/protocol/transcript/mod.rs b/src/protocol/transcript/mod.rs index 2283192..fffeacb 100644 --- a/src/protocol/transcript/mod.rs +++ b/src/protocol/transcript/mod.rs @@ -3,3 +3,29 @@ pub mod verifier; pub use prover::*; pub use verifier::*; + +use effsc::transcript::VerifierTranscript; +use spongefish::{Decoding, Encoding, NargDeserialize, VerifierState}; + +/// Adapter wrapping spongefish's [`VerifierState`] so it implements effsc's +/// [`VerifierTranscript`] trait. Used to plug warp's transcript into +/// [`effsc::verifier::sumcheck_verify`]. +/// +/// The prover-side adapter is a blanket impl inside effsc +/// (`ProverTranscript` on `spongefish::ProverState`); no wrapper needed there. +pub struct EffscVerifierTranscript<'s, 'a>(pub &'s mut VerifierState<'a>); + +impl<'s, 'a, F> VerifierTranscript for EffscVerifierTranscript<'s, 'a> +where + F: ark_ff::Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, +{ + type Error = spongefish::VerificationError; + + fn receive(&mut self) -> Result { + self.0.prover_message::() + } + + fn challenge(&mut self) -> F { + self.0.verifier_message::() + } +} diff --git a/src/protocol/transcript/verifier.rs b/src/protocol/transcript/verifier.rs index 36e58d4..6e06c1e 100644 --- a/src/protocol/transcript/verifier.rs +++ b/src/protocol/transcript/verifier.rs @@ -77,36 +77,36 @@ impl< } } -pub struct DerivedRandomness { +/// Transcript values read BEFORE the twin-constraint sumcheck: PESAT +/// commitment + l1 mus + l1 τs + ω + τ. +pub struct PreTwinConstraint { pub rt_0: MT::InnerDigest, pub l1_mus: Vec, pub l1_taus: Vec>, pub omega: F, pub tau: Vec, - pub gamma_sumcheck: Vec, - pub coeffs_twinc_sumcheck: Vec>, +} + +/// Transcript values read BETWEEN the two sumchecks: new commitment, +/// η, ν₀, OOD samples+answers, shift-query byte challenges, ξ. +pub struct BetweenSumchecks { pub td: MT::InnerDigest, pub eta: F, pub nus: Vec, pub ood_samples: Vec, pub bytes_shift_queries: Vec, pub xi: Vec, - pub alpha_sumcheck: Vec, - pub sums_batching_sumcheck: Vec<[F; 2]>, } -pub fn derive_randomness< +pub fn derive_pre_twin_constraint< F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, MT: Config + From<[u8; 32]>>, >( verifier_state: &mut VerifierState<'_>, l1: usize, - log_n: usize, log_l: usize, - s: usize, - t: usize, log_m: usize, -) -> VerificationResult> { +) -> VerificationResult> { // commitment digest let rt_0_bytes: [u8; 32] = verifier_state.prover_message()?; let rt_0: MT::InnerDigest = rt_0_bytes.into(); @@ -114,7 +114,7 @@ pub fn derive_randomness< // mus let l1_mus: Vec = verifier_state.prover_messages_vec(l1)?; - // challenge taus + // challenge taus (squeezed) let l1_taus: Vec> = (0..l1) .map(|_| { (0..log_m) @@ -128,17 +128,24 @@ pub fn derive_randomness< .map(|_| verifier_state.verifier_message::()) .collect(); - // twin constraints sumcheck - let mut gamma_sumcheck = Vec::new(); - let mut coeffs_twinc_sumcheck = Vec::new(); - for _ in 0..log_l { - let h_coeffs: Vec = - verifier_state.prover_messages_vec(1 + (log_n + 1).max(log_m + 2))?; - let c: F = verifier_state.verifier_message(); - gamma_sumcheck.push(c); - coeffs_twinc_sumcheck.push(h_coeffs); - } + Ok(PreTwinConstraint { + rt_0, + l1_mus, + l1_taus, + omega, + tau, + }) +} +pub fn derive_between_sumchecks< + F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, + MT: Config + From<[u8; 32]>>, +>( + verifier_state: &mut VerifierState<'_>, + log_n: usize, + s: usize, + t: usize, +) -> VerificationResult> { // td digest let td_bytes: [u8; 32] = verifier_state.prover_message()?; let td: MT::InnerDigest = td_bytes.into(); @@ -158,7 +165,7 @@ pub fn derive_randomness< let ood_answers: Vec = verifier_state.prover_messages_vec(s)?; nus.extend(ood_answers); - // shift queries and zero check + // shift queries and ξ let r = 1 + s + t; let log_r = log2(r) as usize; let n_shift_queries = (t * log_n).div_ceil(8); @@ -169,31 +176,12 @@ pub fn derive_randomness< .map(|_| verifier_state.verifier_message::()) .collect(); - // batching sumcheck - let mut alpha_sumcheck = Vec::new(); - let mut sums_batching_sumcheck = Vec::new(); - for _ in 0..log_n { - let sums: [F; 2] = verifier_state.prover_messages()?; - let c: F = verifier_state.verifier_message(); - alpha_sumcheck.push(c); - sums_batching_sumcheck.push(sums); - } - - Ok(DerivedRandomness { - rt_0, - l1_mus, - l1_taus, - omega, - tau, - gamma_sumcheck, - coeffs_twinc_sumcheck, + Ok(BetweenSumchecks { td, eta, nus, ood_samples, bytes_shift_queries, xi, - alpha_sumcheck, - sums_batching_sumcheck, }) } diff --git a/src/relations/r1cs/mod.rs b/src/relations/r1cs/mod.rs index 6377cfe..a23ee4f 100644 --- a/src/relations/r1cs/mod.rs +++ b/src/relations/r1cs/mod.rs @@ -2,7 +2,7 @@ pub mod hashchain; use ark_ff::Field; use ark_relations::gr1cs::ConstraintSystemRef; -use efficient_sumcheck::{hypercube::Hypercube, order_strategy::AscendingOrder}; +use effsc::hypercube::Ascending; use crate::error::WARPError; @@ -99,10 +99,9 @@ impl BundledPESAT for R1CS { type Constraints = R1CSConstraints; fn evaluate_bundled(&self, zero_evader_evals: &[F], z: &[F]) -> Result { - let mut cube = Hypercube::::new(self.log_m); - // TODO: multithread this - cube.try_fold(F::ZERO, |acc, (index, _point)| { + Ascending::new(self.log_m).try_fold(F::ZERO, |acc, p| { + let index = p.index; let eq_tau_i = *zero_evader_evals .get(index) .ok_or(WARPError::ZeroEvaderSize(zero_evader_evals.len(), index))?; diff --git a/src/utils/poly.rs b/src/utils/poly.rs index b477c1c..efefdde 100644 --- a/src/utils/poly.rs +++ b/src/utils/poly.rs @@ -1,19 +1,19 @@ use ark_ff::Field; -use efficient_sumcheck::{ - hypercube::HypercubeMember, interpolation::LagrangePolynomial, order_strategy::AscendingOrder, -}; -pub fn eq_poly(original_tau: &[F], point: usize) -> F { - // TODO (z-tech): will fix and get rid of this function - let num_variables = original_tau.len(); - let mut tau = original_tau.to_vec(); - tau.reverse(); - let tau_hat: Vec = tau.iter().map(|t| F::ONE - *t).collect(); - LagrangePolynomial::::lag_poly( - tau, - tau_hat, - HypercubeMember::new(num_variables, point), - ) +/// `eq(τ, y)` where `y ∈ {0,1}^{τ.len()}` is the Boolean point whose bit `j` +/// is `(point >> j) & 1`. Matches the LSB-indexed formula used by +/// [`effsc::hypercube::compute_hypercube_eq_evals`], i.e. +/// `eq_poly(τ, i) == compute_hypercube_eq_evals(τ.len(), τ)[i]`. +pub fn eq_poly(tau: &[F], point: usize) -> F { + let num_variables = tau.len(); + (0..num_variables).fold(F::one(), |acc, j| { + let bit = (point >> j) & 1; + if bit == 1 { + acc * tau[j] + } else { + acc * (F::one() - tau[j]) + } + }) } pub fn eq_poly_non_binary(x: &[F], y: &[F]) -> F { diff --git a/tests/integration_warp.rs b/tests/integration_warp.rs index 98c2cb5..17086cf 100644 --- a/tests/integration_warp.rs +++ b/tests/integration_warp.rs @@ -85,14 +85,13 @@ fn warp_test() { .unwrap(); let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = - WARP::, _, Blake3MerkleConfig>::new( - warp_config.clone(), - code.clone(), - r1cs.clone(), - (), - (), - ); + let hash_chain_warp = WARP::, _, Blake3MerkleConfig>::new( + warp_config.clone(), + code.clone(), + r1cs.clone(), + (), + (), + ); let (mut acc_roots, mut acc_alphas, mut acc_mus, mut acc_taus, mut acc_xs, mut acc_eta) = (vec![], vec![], vec![], vec![], vec![], vec![]); @@ -100,7 +99,7 @@ fn warp_test() { for _ in 0..l1 { let domainsep = spongefish::domain_separator!("test::warp"); - let mut prover_state = domainsep.instance(&0u32).std_prover(); + let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), _pf) = hash_chain_warp .prove( (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), @@ -127,16 +126,15 @@ fn warp_test() { let warp_config = WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = - WARP::, _, Blake3MerkleConfig>::new( - warp_config.clone(), - code.clone(), - r1cs.clone(), - (), - (), - ); + let hash_chain_warp = WARP::, _, Blake3MerkleConfig>::new( + warp_config.clone(), + code.clone(), + r1cs.clone(), + (), + (), + ); - let mut prover_state = domainsep.instance(&0u32).std_prover(); + let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), pf) = hash_chain_warp .prove( (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), @@ -160,7 +158,10 @@ fn warp_test() { let narg_str = prover_state.narg_string().to_vec(); let domainsep_v = spongefish::domain_separator!("test::warp"); - let mut verifier_state = domainsep_v.instance(&0u32).std_verifier(&narg_str); + let mut verifier_state = domainsep_v + .without_session() + .instance(&0u32) + .std_verifier(&narg_str); hash_chain_warp .verify( (r1cs.m, r1cs.n, r1cs.k), @@ -207,8 +208,7 @@ fn warp_test_goldilocks() { hash_chain_size, )) .unwrap(); - let code_config = - ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); let code = ReedSolomon::new(code_config); let instances_witnesses: (Vec>, Vec>) = (0..l1) @@ -256,7 +256,7 @@ fn warp_test_goldilocks() { for _ in 0..l1 { let domainsep = spongefish::domain_separator!("test::warp"); - let mut prover_state = domainsep.instance(&0u32).std_prover(); + let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), _pf) = hash_chain_warp .prove( (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), @@ -293,7 +293,7 @@ fn warp_test_goldilocks() { (), ); - let mut prover_state = domainsep.instance(&0u32).std_prover(); + let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), pf) = hash_chain_warp .prove( (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), @@ -317,7 +317,10 @@ fn warp_test_goldilocks() { let narg_str = prover_state.narg_string().to_vec(); let domainsep_v = spongefish::domain_separator!("test::warp"); - let mut verifier_state = domainsep_v.instance(&0u32).std_verifier(&narg_str); + let mut verifier_state = domainsep_v + .without_session() + .instance(&0u32) + .std_verifier(&narg_str); hash_chain_warp .verify( (r1cs.m, r1cs.n, r1cs.k), diff --git a/tests/profile_json.rs b/tests/profile_json.rs index 0e6bbf2..5a7d6ff 100644 --- a/tests/profile_json.rs +++ b/tests/profile_json.rs @@ -52,7 +52,10 @@ impl Write for SharedBuf { fn json_layer_emits_phase_records() { let sink = SharedBuf(Arc::new(Mutex::new(Vec::new()))); let installed = warp::profile::init_json(sink.clone()); - assert!(installed, "json subscriber install should succeed on first call"); + assert!( + installed, + "json subscriber install should succeed on first call" + ); // Minimum viable prove run — same shape as the top-level warp_test. let l1 = 4; @@ -93,17 +96,16 @@ fn json_layer_emits_phase_records() { .unzip(); let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = - WARP::, _, Blake3MerkleConfig>::new( - warp_config, - code, - r1cs.clone(), - (), - (), - ); + let hash_chain_warp = WARP::, _, Blake3MerkleConfig>::new( + warp_config, + code, + r1cs.clone(), + (), + (), + ); let domainsep = spongefish::domain_separator!("test::profile_json"); - let mut prover_state = domainsep.instance(&0u32).std_prover(); + let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); hash_chain_warp .prove( @@ -119,7 +121,10 @@ fn json_layer_emits_phase_records() { // Inspect collected records. let bytes = sink.0.lock().unwrap().clone(); let text = String::from_utf8(bytes).expect("JSON output is UTF-8"); - assert!(!text.is_empty(), "JSON sink should contain at least one record"); + assert!( + !text.is_empty(), + "JSON sink should contain at least one record" + ); let lines: Vec<&str> = text.lines().collect(); assert!( @@ -133,8 +138,14 @@ fn json_layer_emits_phase_records() { line.contains(r#""schema":"warp.profile.v1""#), "line {i} missing schema: {line}" ); - assert!(line.contains(r#""wall_ns""#), "line {i} missing wall_ns: {line}"); - assert!(line.contains(r#""counters""#), "line {i} missing counters: {line}"); + assert!( + line.contains(r#""wall_ns""#), + "line {i} missing wall_ns: {line}" + ); + assert!( + line.contains(r#""counters""#), + "line {i} missing counters: {line}" + ); assert!( line.contains(r#""dimensions""#), "line {i} missing dimensions: {line}" diff --git a/tests/verifier_negative.rs b/tests/verifier_negative.rs index fe44b3e..06ddef8 100644 --- a/tests/verifier_negative.rs +++ b/tests/verifier_negative.rs @@ -61,9 +61,16 @@ struct Fixture { } impl Fixture { - fn verify(&self, acc_x: AccumulatorInstance, proof: WARPProof) -> Result<(), VerifierError> { + fn verify( + &self, + acc_x: AccumulatorInstance, + proof: WARPProof, + ) -> Result<(), VerifierError> { let domainsep_v = spongefish::domain_separator!("test::warp::negative"); - let mut verifier_state = domainsep_v.instance(&0u32).std_verifier(&self.narg_str); + let mut verifier_state = domainsep_v + .without_session() + .instance(&0u32) + .std_verifier(&self.narg_str); self.warp.verify(self.vk, &mut verifier_state, acc_x, proof) } } @@ -119,7 +126,7 @@ fn make_fixture() -> Fixture { for _ in 0..l1 { let ds = spongefish::domain_separator!("test::warp::negative"); - let mut ps = ds.instance(&0u32).std_prover(); + let mut ps = ds.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), _) = w1 .prove( (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), @@ -146,7 +153,7 @@ fn make_fixture() -> Fixture { let warp = WARP::, _, MT>::new(warp_cfg2, code, r1cs.clone(), (), ()); let ds = spongefish::domain_separator!("test::warp::negative"); - let mut ps = ds.instance(&0u32).std_prover(); + let mut ps = ds.without_session().instance(&0u32).std_prover(); let ((acc_x, _acc_w), proof) = warp .prove( (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), @@ -182,10 +189,7 @@ fn assert_err(result: Result<(), VerifierError>, expected: &str) { Ok(()) => panic!("expected `{expected}`, got Ok(())"), Err(err) => { let dbg = format!("{err:?}"); - assert!( - dbg.contains(expected), - "expected `{expected}`, got `{dbg}`" - ); + assert!(dbg.contains(expected), "expected `{expected}`, got `{dbg}`"); } } } From 02b7e13cba1d73a650c619188a041902ebe58d61 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Sun, 19 Apr 2026 16:49:15 +0200 Subject: [PATCH 18/68] fix conflicts --- Cargo.toml | 11 ---- Makefile | 48 --------------- benches/README.md | 62 ------------------- benches/docker/Dockerfile.iai | 26 -------- benches/iai_phases.rs | 108 ---------------------------------- 5 files changed, 255 deletions(-) delete mode 100644 Makefile delete mode 100644 benches/README.md delete mode 100644 benches/docker/Dockerfile.iai delete mode 100644 benches/iai_phases.rs diff --git a/Cargo.toml b/Cargo.toml index 5b4084b..e67a5f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,11 +48,6 @@ spongefish = { git = "https://github.com/z-tech/spongefish.git", branch = "small ark-bls12-381 = "0.5.0" ark-bn254 = "0.5.0" criterion = "0.8" -# Deterministic instruction-count benches. Requires valgrind + the -# `iai-callgrind-runner` binary at runtime (see benches/README.md). -# Unused on macOS except via Docker — `cargo build --bench iai_phases` -# still works everywhere, only `cargo bench` fails without valgrind. -iai-callgrind = "0.14" [features] default = ["asm"] @@ -68,9 +63,3 @@ profile = ["dep:tracing-subscriber", "dep:libc"] [[bench]] name = "warp_rs" harness = false - -# iai-callgrind: deterministic instruction counts per prove phase. Runs -# under valgrind; use `make bench-ci-local` on macOS to run inside Docker. -[[bench]] -name = "iai_phases" -harness = false diff --git a/Makefile b/Makefile deleted file mode 100644 index 91be7a9..0000000 --- a/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# Convenience targets for Plan B (benchmarking + regression detection). -# Plan 0 / O deliverables are consumed via cargo directly — no make target -# needed. - -.PHONY: help test clippy bench-wall bench-ci bench-ci-local bench-docker-build - -help: - @echo "Targets:" - @echo " test cargo test (both feature configs)" - @echo " clippy cargo clippy --all-targets --all-features" - @echo " bench-wall criterion wall-time benches (runs anywhere)" - @echo " bench-ci iai-callgrind instruction-count benches (Linux native)" - @echo " bench-ci-local iai-callgrind benches inside Docker (macOS-friendly)" - -test: - cargo test - cargo test --features profile - -clippy: - cargo clippy --all-targets -- -D warnings - cargo clippy --all-targets --all-features -- -D warnings - -bench-wall: - cargo bench --bench warp_rs - -# Native iai-callgrind bench. Requires valgrind + iai-callgrind-runner -# installed on the host. Will fail on macOS (valgrind is unsupported -# since Big Sur) — use `bench-ci-local` instead. -bench-ci: - cargo bench --bench iai_phases - -IAI_IMAGE := warp-iai-bench -IAI_CACHE := $(CURDIR)/target/iai-docker-cache - -bench-docker-build: - docker build -f benches/docker/Dockerfile.iai -t $(IAI_IMAGE) . - -# Run the iai bench inside a Linux container. Mounts the repo read-write -# so the target/ dir (including bench output) is reused across runs. -# Caches cargo registry + git + target in host-side directories to avoid -# re-downloading arkworks on every invocation. -bench-ci-local: bench-docker-build - mkdir -p $(IAI_CACHE)/registry $(IAI_CACHE)/git - docker run --rm \ - -v $(CURDIR):/workspace \ - -v $(IAI_CACHE)/registry:/usr/local/cargo/registry \ - -v $(IAI_CACHE)/git:/usr/local/cargo/git \ - $(IAI_IMAGE) diff --git a/benches/README.md b/benches/README.md deleted file mode 100644 index 7b0f118..0000000 --- a/benches/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Warp benches - -Two bench suites with different roles: - -| Bench | Signal | Scope | Cost | -|--------------------------|--------------------------|----------------------------|------------------| -| `warp_rs` (criterion) | Wall time, noisy | Whole `prove` at 5 sizes | Fast, informational | -| `iai_phases` (iai-callgrind) | Instruction count, deterministic | Whole `prove` at 1 size (v1) | Slow, CI gate | - -The criterion suite is for local feedback — it reports wall time in -milliseconds, which is human-intuitive but varies across machines and -loads. It runs natively on any host. - -The iai-callgrind suite is for **regression detection**. Callgrind counts -executed instructions, so the number is reproducible across CI runs. A -1% change is real signal. Plan B uses it as the PR gate. - -## Running - -### Criterion (works on macOS, Linux, Windows) - -```bash -make bench-wall -# or -cargo bench --bench warp_rs -``` - -### iai-callgrind (native — requires valgrind) - -```bash -cargo install iai-callgrind-runner --version 0.14.0 -make bench-ci -# or -cargo bench --bench iai_phases -``` - -### iai-callgrind (macOS via Docker) - -Valgrind hasn't worked on macOS since Big Sur, so on a Mac host run the -bench inside a Linux container: - -```bash -make bench-ci-local -``` - -This builds `benches/docker/Dockerfile.iai` (cached after the first -run), mounts the repo read-write, and caches cargo registry + git in -`target/iai-docker-cache/` so subsequent runs don't re-download -arkworks. First run: ~5 min build + ~5 min bench. Later runs: ~30 s -build check + bench time. - -## v1 scope - -`iai_phases` currently benches **one** prove configuration -(`l1=4, s=2, t=7, hashchain=10`). Larger parameter points and -per-phase attribution are deferred — see the docstring at the top of -`iai_phases.rs` for why. - -Baseline instruction counts are not yet committed; the plan is to -capture them in a follow-up once the CI workflow is wired up. See -`~/.claude/plans/nested-conjuring-scott.md` → Plan B for the full -roadmap, and the TODO about a GitHub Actions workflow. diff --git a/benches/docker/Dockerfile.iai b/benches/docker/Dockerfile.iai deleted file mode 100644 index ff92433..0000000 --- a/benches/docker/Dockerfile.iai +++ /dev/null @@ -1,26 +0,0 @@ -# Dockerfile for running iai-callgrind benches on hosts without valgrind -# (primarily macOS). Build + run via the repo-root Makefile: -# -# make bench-ci-local -# -# The image caches the iai-callgrind-runner binary + valgrind so repeat -# runs only re-build the warp crate. -FROM rust:1.81-slim-bookworm - -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - valgrind \ - pkg-config \ - libssl-dev \ - ca-certificates \ - git \ - && rm -rf /var/lib/apt/lists/* - -# Match the version in Cargo.toml dev-dependencies. -ARG IAI_RUNNER_VERSION=0.14.0 -RUN cargo install iai-callgrind-runner --version ${IAI_RUNNER_VERSION} --locked - -WORKDIR /workspace - -# Default: run the bench. The repo is expected to be mounted at /workspace. -CMD ["cargo", "bench", "--bench", "iai_phases"] diff --git a/benches/iai_phases.rs b/benches/iai_phases.rs deleted file mode 100644 index 2e9a976..0000000 --- a/benches/iai_phases.rs +++ /dev/null @@ -1,108 +0,0 @@ -//! iai-callgrind benchmarks measuring deterministic instruction counts -//! per prove call at fixed parameter points. -//! -//! Unlike [`benches/warp_rs.rs`] (criterion, wall time), these numbers -//! are reproducible across machines: callgrind counts executed -//! instructions, not time. A 1% change is real signal. -//! -//! Runs under valgrind. On macOS valgrind is not available natively — -//! use `make bench-ci-local` which invokes Docker (see benches/README.md). -//! -//! Installation: -//! cargo install iai-callgrind-runner --version 0.14.0 -//! then -//! cargo bench --bench iai_phases -//! -//! **v1 scope**: one size (l1=4, hashchain=10). Setup (encoding, -//! poseidon config build, instance gen) is measured inside the bench -//! because plumbing the `setup = expr` attribute through -//! parameterised `#[bench::...]` cases didn't resolve cleanly on -//! iai-callgrind 0.14 — the macro failed to see the target function -//! from the bench-crate root. Revisit when adding more parameter -//! points; a working pattern is either the non-parameterised form -//! below, or `iai_callgrind::BinaryBenchmarkConfig`. - -use ark_bls12_381::Fr as BLS12_381; -use ark_codes::{ - reed_solomon::{config::ReedSolomonConfig, ReedSolomon}, - traits::LinearCode, -}; -use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; -use ark_std::rand::thread_rng; - -use iai_callgrind::{black_box, library_benchmark, library_benchmark_group, main}; -use warp::config::WARPConfig; -use warp::relations::BundledPESAT; -use warp::traits::AccumulationScheme as _; -use warp::types::{AccumulatorInstance, AccumulatorWitness}; -use warp::WARP; - -mod utils; -use utils::domainsep::init_prover_state; -use utils::hash_chain::{get_hashchain_instance_witness_pairs, get_hashchain_r1cs}; -use utils::poseidon; - -type F = BLS12_381; - -/// Output of [`setup_prove`]: everything needed to call `warp.prove` once, -/// assembled in setup time (NOT counted in the measurement). -struct ProveInputs { - warp: WARP, ReedSolomon, Blake3MerkleConfig>, - pk: (warp::relations::r1cs::R1CS, usize, usize, usize), - prover_state: spongefish::ProverState, - instances: Vec>, - witnesses: Vec>, -} - -fn setup_prove(l: usize, s: usize, t: usize, hashchain_size: usize) -> ProveInputs { - let mut rng = thread_rng(); - let poseidon_config = poseidon::initialize_poseidon_config::(); - let r1cs = get_hashchain_r1cs(&poseidon_config, hashchain_size); - - let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); - let code = ReedSolomon::new(code_config); - - let warp_config = WARPConfig::new(l, l, s, t, r1cs.config(), code.code_len()); - let warp = WARP::>::new(warp_config, code, r1cs.clone(), (), ()); - - let (instances, witnesses) = - get_hashchain_instance_witness_pairs(l, &poseidon_config, hashchain_size, &mut rng); - - ProveInputs { - warp, - pk: (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), - prover_state: init_prover_state(), - instances, - witnesses, - } -} - -fn run_prove(mut inputs: ProveInputs) { - black_box( - inputs - .warp - .prove( - inputs.pk, - &mut inputs.prover_state, - inputs.witnesses, - inputs.instances, - AccumulatorInstance::empty(), - AccumulatorWitness::empty(), - ) - .unwrap(), - ); -} - -// Smallest configuration — matches the unit-test shape (l1=4, hashchain=10). -#[library_benchmark] -fn bench_prove_small() { - let inputs = setup_prove(4, 2, 7, 10); - run_prove(black_box(inputs)); -} - -library_benchmark_group!( - name = prove_benches; - benchmarks = bench_prove_small -); - -main!(library_benchmark_groups = prove_benches); From 0c00fcc143720a414d9177fcebd68068007673cd Mon Sep 17 00:00:00 2001 From: Andrew Zitek-Estrada <1497456+z-tech@users.noreply.github.com> Date: Tue, 28 Apr 2026 13:48:59 +0200 Subject: [PATCH 19/68] Apply suggestion from @winderica Co-authored-by: winderica --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 2a652d5..c6c642d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -723,7 +723,7 @@ impl< let betas = l2_taus .into_iter() .chain(l1_taus) - .zip(l2_xs.clone().into_iter().chain(l1_xs)) + .zip(l2_xs.into_iter().chain(l1_xs)) .map(|(tau_i, x)| concat_slices(&tau_i, &x)) .collect::>>(); let beta = scale_and_sum(&betas, &gamma_eq_evals); From 375da83cce394b01e379a7b348f9205eaf03f533 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Sat, 2 May 2026 12:59:33 +0200 Subject: [PATCH 20/68] bump effsc w/ spongefish 0.7.0 --- Cargo.toml | 38 +++++++-------------- src/crypto/merkle/blake3.rs | 12 +++---- src/lib.rs | 15 ++++---- src/relations/description.rs | 37 +++++++++++--------- src/relations/r1cs/hashchain/mod.rs | 2 +- src/relations/r1cs/hashchain/relation.rs | 10 +++--- src/relations/r1cs/hashchain/synthesizer.rs | 2 +- src/relations/r1cs/mod.rs | 25 +++++++++----- src/serialize.rs | 5 ++- 9 files changed, 74 insertions(+), 72 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dfbec31..c79b099 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,42 +5,30 @@ include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] edition = "2021" [dependencies] -ark-crypto-primitives = { version = "0.5.0", features = [ +ark-crypto-primitives = { version = "0.6.0", features = [ "merkle_tree", "crh", - "r1cs", + "constraints", "sponge", + "blake3", ] } -ark-ff = "0.5.0" -ark-poly = "0.5.0" -ark-r1cs-std = "0.5.0" -ark-relations = "0.5.0" -ark-serialize = "0.5.0" -ark-std = "0.5.0" +ark-ff = "0.6.0" +ark-poly = "0.6.0" +ark-r1cs-std = "0.6.0" +ark-relations = "0.6.0" +ark-serialize = "0.6.0" +ark-std = "0.6.0" blake3 = "1.5.0" serde = { version = "1.0.0", features = ["derive"] } serde_json = "1.0" -spongefish = { git = "https://github.com/z-tech/spongefish.git", branch = "smallfp-support", features = [ - "ark-ff", -] } +spongefish = { version = "0.7.0", features = ["ark-ff"] } effsc = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git" } thiserror = "2.0.16" -ark-codes = { git = "https://github.com/dmpierre/ark-codes.git" } - - -[patch.crates-io] -ark-crypto-primitives = { git = "https://github.com/benbencik/crypto-primitives.git", branch = "smallfp-absorb-trait" } -ark-ff = { git = "https://github.com/arkworks-rs/algebra.git" } -ark-poly = { git = "https://github.com/arkworks-rs/algebra.git" } -ark-serialize = { git = "https://github.com/arkworks-rs/algebra.git" } - -# resolve transitive pull of spongefish from efficient-sumcheck -[patch."https://github.com/arkworks-rs/spongefish"] -spongefish = { git = "https://github.com/z-tech/spongefish.git", branch = "smallfp-support" } +ark-codes = { git = "https://github.com/z-tech/ark-codes.git", branch = "z-tech/arkworks-0.6.0" } [dev-dependencies] -ark-bls12-381 = "0.5.0" -ark-bn254 = "0.5.0" +ark-bls12-381 = "0.6.0" +ark-bn254 = "0.6.0" criterion = "0.8" [features] diff --git a/src/crypto/merkle/blake3.rs b/src/crypto/merkle/blake3.rs index 963a7d4..d2ef41b 100644 --- a/src/crypto/merkle/blake3.rs +++ b/src/crypto/merkle/blake3.rs @@ -1,7 +1,6 @@ use super::parameters::MerkleTreeParams; -use ark_crypto_primitives::crh::blake3::fields::Blake3F; -use ark_crypto_primitives::crh::blake3::Blake3; -use ark_crypto_primitives::crh::blake3::GenericDigest; +use ark_crypto_primitives::crh::blake3::{Blake3CRH, Blake3TwoToOneCRH}; +use ark_crypto_primitives::crh::ByteDigest; use ark_crypto_primitives::{ crh::{CRHScheme, TwoToOneCRHScheme}, merkle_tree::{Config as MerkleConfig, IdentityDigestConverter}, @@ -15,13 +14,14 @@ pub struct Blake3MerkleConfig { _field: PhantomData, } -pub type Blake3MerkleTreeParams = MerkleTreeParams, Blake3, GenericDigest<32>>; +pub type Blake3MerkleTreeParams = + MerkleTreeParams, Blake3TwoToOneCRH, ByteDigest<32>>; impl MerkleConfig for Blake3MerkleConfig { type Leaf = [F]; type LeafDigest = ::Output; type LeafInnerDigestConverter = IdentityDigestConverter; type InnerDigest = ::Output; - type LeafHash = Blake3F; // blake3, over field elements - type TwoToOneHash = Blake3; + type LeafHash = Blake3CRH; + type TwoToOneHash = Blake3TwoToOneCRH; } diff --git a/src/lib.rs b/src/lib.rs index c6c642d..30bf5e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -694,20 +694,20 @@ impl< // `final_claim == μ · Σ eq(ζ_i, α_lsb)·ξ_eq(i)`. MSB half-split // → reverse the challenge vector once before feeding to // eq_poly_non_binary (arkworks MLE convention). - let (alpha_sumcheck_msb, batching_final_claim) = { + let (alpha_sumcheck, batching_final_claim) = { let mut wrap = EffscVerifierTranscript(verifier_state); - let res = sumcheck_verify(sigma_2, 2, log_n, &mut wrap, |_, _| Ok(()))?; + let mut res = sumcheck_verify(sigma_2, 2, log_n, &mut wrap, |_, _| Ok(()))?; + res.challenges.reverse(); (res.challenges, res.final_claim) }; - let alpha_sumcheck_lsb: Vec = alpha_sumcheck_msb.iter().rev().copied().collect(); let mut zeta_eqs = Vec::with_capacity(r); - zeta_eqs.push(eq_poly_non_binary(&zeta_0, &alpha_sumcheck_lsb)); + zeta_eqs.push(eq_poly_non_binary(&zeta_0, &alpha_sumcheck)); for chunk in ood_samples.chunks(log_n) { - zeta_eqs.push(eq_poly_non_binary(chunk, &alpha_sumcheck_lsb)); + zeta_eqs.push(eq_poly_non_binary(chunk, &alpha_sumcheck)); } for zeta in &binary_shift_queries { - zeta_eqs.push(eq_poly_non_binary(zeta, &alpha_sumcheck_lsb)); + zeta_eqs.push(eq_poly_non_binary(zeta, &alpha_sumcheck)); } debug_assert_eq!(zeta_eqs.len(), r); let expected_batching = acc_instance.2[0] @@ -718,7 +718,7 @@ impl< (expected_batching == batching_final_claim).ok_or_err(VerifierError::Target)?; // 10. Accumulator consistency: new α and β. - (acc_instance.1[0] == alpha_sumcheck_lsb).ok_or_err(VerifierError::CodeEvaluationPoint)?; + (acc_instance.1[0] == alpha_sumcheck).ok_or_err(VerifierError::CodeEvaluationPoint)?; let betas = l2_taus .into_iter() @@ -748,7 +748,6 @@ impl< )?; (rt[0] == computed_mt.root()).ok_or_err(DeciderError::MerkleRoot)?; (mt[0].root() == computed_mt.root()).ok_or_err(DeciderError::MerkleTrapDoor)?; - (mt[0].leaf_nodes == computed_mt.leaf_nodes).ok_or_err(DeciderError::MerkleRoot)?; let f_hat = DenseMultilinearExtension::from_evaluations_slice( log2(self.code.code_len()) as usize, diff --git a/src/relations/description.rs b/src/relations/description.rs index 032dfeb..ded163e 100644 --- a/src/relations/description.rs +++ b/src/relations/description.rs @@ -1,5 +1,5 @@ use ark_ff::Field; -use ark_relations::r1cs::{ConstraintMatrices, ConstraintSynthesizer, ConstraintSystem}; +use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystem, R1CS_PREDICATE_LABEL}; use serde::Serialize; #[derive(Serialize)] @@ -37,22 +37,27 @@ impl SerializableConstraintMatrices { .generate_constraints(constraint_system.clone()) .unwrap(); constraint_system.finalize(); - let matrices: ConstraintMatrices = constraint_system.to_matrices().unwrap(); - let serializable = SerializableConstraintMatrices::from(matrices); + + let num_instance_variables = constraint_system.num_instance_variables(); + let num_witness_variables = constraint_system.num_witness_variables(); + let num_constraints = constraint_system.num_constraints(); + + let mut matrices = constraint_system.to_matrices().unwrap(); + let mut r1cs = matrices.remove(R1CS_PREDICATE_LABEL).unwrap(); + let mut r1cs_iter = r1cs.drain(..); + let a = r1cs_iter.next().unwrap(); + let b = r1cs_iter.next().unwrap(); + let c = r1cs_iter.next().unwrap(); + + let serializable = SerializableConstraintMatrices { + num_instance_variables, + num_witness_variables, + num_constraints, + a: SerializableConstraintMatrices::serialize_nested_field(a), + b: SerializableConstraintMatrices::serialize_nested_field(b), + c: SerializableConstraintMatrices::serialize_nested_field(c), + }; let serialized = serde_json::to_string(&serializable).unwrap(); serialized.into_bytes() } } - -impl From> for SerializableConstraintMatrices { - fn from(m: ConstraintMatrices) -> Self { - Self { - num_instance_variables: m.num_instance_variables, - num_witness_variables: m.num_witness_variables, - num_constraints: m.num_constraints, - a: SerializableConstraintMatrices::serialize_nested_field(m.a), - b: SerializableConstraintMatrices::serialize_nested_field(m.b), - c: SerializableConstraintMatrices::serialize_nested_field(m.c), - } - } -} diff --git a/src/relations/r1cs/hashchain/mod.rs b/src/relations/r1cs/hashchain/mod.rs index 45c664c..4f34fe3 100644 --- a/src/relations/r1cs/hashchain/mod.rs +++ b/src/relations/r1cs/hashchain/mod.rs @@ -12,7 +12,7 @@ use ark_crypto_primitives::{ }; use ark_ff::PrimeField; use ark_r1cs_std::fields::fp::FpVar; -use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; +use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystem}; pub use config::HashChainConfig; pub use instance::HashChainInstance; pub use relation::compute_hash_chain; diff --git a/src/relations/r1cs/hashchain/relation.rs b/src/relations/r1cs/hashchain/relation.rs index 6742e0a..2bd4999 100644 --- a/src/relations/r1cs/hashchain/relation.rs +++ b/src/relations/r1cs/hashchain/relation.rs @@ -4,7 +4,7 @@ use ark_crypto_primitives::{ }; use ark_ff::{Field, PrimeField}; use ark_r1cs_std::fields::fp::FpVar; -use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef}; +use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef}; use ark_serialize::CanonicalSerialize; use ark_std::marker::PhantomData; @@ -95,13 +95,15 @@ where constraint_system.finalize(); let cs = constraint_system.into_inner().unwrap(); + let x = cs.instance_assignment().unwrap().to_vec(); + let w = cs.witness_assignment().unwrap().to_vec(); Self { - constraint_system: ConstraintSystemRef::new(cs.clone()), + constraint_system: ConstraintSystemRef::new(cs), config: hash_config, instance, witness, - x: cs.instance_assignment, - w: cs.witness_assignment, + x, + w, _crhs_scheme: PhantomData, _crhs_scheme_gadget: PhantomData, } diff --git a/src/relations/r1cs/hashchain/synthesizer.rs b/src/relations/r1cs/hashchain/synthesizer.rs index beb4a90..511b1ff 100644 --- a/src/relations/r1cs/hashchain/synthesizer.rs +++ b/src/relations/r1cs/hashchain/synthesizer.rs @@ -1,7 +1,7 @@ use ark_crypto_primitives::crh::{CRHScheme, CRHSchemeGadget}; use ark_ff::{Field, PrimeField}; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, fields::fp::FpVar}; -use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; +use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError}; use ark_std::marker::PhantomData; use crate::relations::r1cs::hashchain::{HashChainInstance, HashChainWitness}; diff --git a/src/relations/r1cs/mod.rs b/src/relations/r1cs/mod.rs index 72c274f..b6bd5d9 100644 --- a/src/relations/r1cs/mod.rs +++ b/src/relations/r1cs/mod.rs @@ -1,7 +1,7 @@ pub mod hashchain; use ark_ff::Field; -use ark_relations::r1cs::ConstraintSystemRef; +use ark_relations::gr1cs::{ConstraintSystemRef, R1CS_PREDICATE_LABEL}; use effsc::hypercube::Ascending; use crate::error::WARPError; @@ -27,21 +27,30 @@ impl TryFrom> for R1CS { type Error = WARPError; fn try_from(cs: ConstraintSystemRef) -> Result { - let matrices = cs.to_matrices().unwrap(); + let mut matrices = cs.to_matrices().unwrap(); + let mut r1cs = matrices.remove(R1CS_PREDICATE_LABEL).unwrap(); + let mut r1cs_iter = r1cs.drain(..); + let a_mat = r1cs_iter.next().unwrap(); + let b_mat = r1cs_iter.next().unwrap(); + let c_mat = r1cs_iter.next().unwrap(); + + let num_constraints = cs.num_constraints(); + let num_instance_variables = cs.num_instance_variables(); + let num_witness_variables = cs.num_witness_variables(); // number of constraints should be to be power of 2 - let m = matrices.num_constraints.next_power_of_two(); - let n = matrices.num_instance_variables + matrices.num_witness_variables; - let k = matrices.num_witness_variables; + let m = num_constraints.next_power_of_two(); + let n = num_instance_variables + num_witness_variables; + let k = num_witness_variables; // both `unwrap()` calls below are safe since warp/lib.rs forbids compiling on platforms // with 16-bits pointers width let log_m = m.ilog2().try_into().unwrap(); let log_n = n.ilog2().try_into().unwrap(); - let mut a = matrices.a.into_iter(); - let mut b = matrices.b.into_iter(); - let mut c = matrices.c.into_iter(); + let mut a = a_mat.into_iter(); + let mut b = b_mat.into_iter(); + let mut c = c_mat.into_iter(); let mut p = vec![]; for _ in 0..m { // when there are no constraints left, we store an empty one diff --git a/src/serialize.rs b/src/serialize.rs index e03ceff..d9846ff 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -29,7 +29,7 @@ pub struct AccWitnessSerializer< F: Field + PrimeField, MT: Config + From<[u8; 32]>>, > { - pub td: Vec, + pub rt: MT::InnerDigest, pub f: Vec, pub w: Vec, } @@ -42,10 +42,9 @@ impl + Fr assert_eq!(acc_witness.1.len(), 1); assert_eq!(acc_witness.2.len(), 1); let f = acc_witness.1[0].clone(); - assert_eq!(f.len(), acc_witness.0[0].leaf_nodes.len()); let w = acc_witness.2[0].clone(); Self { - td: acc_witness.0[0].clone().leaf_nodes, + rt: acc_witness.0[0].root(), f, w, } From d135f8d8e4c4d80af7297b6122a4fd8c2dedd3fa Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Sun, 3 May 2026 16:34:42 +0200 Subject: [PATCH 21/68] merge --- src/protocol/phases/twin_constraint.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/protocol/phases/twin_constraint.rs b/src/protocol/phases/twin_constraint.rs index 5f0245f..b190ea4 100644 --- a/src/protocol/phases/twin_constraint.rs +++ b/src/protocol/phases/twin_constraint.rs @@ -103,9 +103,8 @@ impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { .enumerate() .map(|(i, (a, b, c))| { let eq = eq_poly(b_even, i); - let eval = |lc: &[(F, usize)]| { - lc.iter().map(|(t, idx)| z_even[*idx] * t).sum::() - }; + let eval = + |lc: &[(F, usize)]| lc.iter().map(|(t, idx)| z_even[*idx] * t).sum::(); eq * (eval(a) * eval(b) - eval(c)) }) .sum::(); From 72a8ee4652a5cabbf638431ce1f6597cbe86512b Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Sun, 3 May 2026 17:47:49 +0200 Subject: [PATCH 22/68] phases --- src/lib.rs | 92 +++++----- src/protocol/phases/batching.rs | 123 +++++++------- src/protocol/phases/mod.rs | 58 +++++-- src/protocol/phases/ood.rs | 45 ++--- src/protocol/phases/pesat.rs | 119 +++++++------ src/protocol/phases/proximity.rs | 225 ++++++++++++++----------- src/protocol/phases/twin_constraint.rs | 176 ++++++++++--------- 7 files changed, 482 insertions(+), 356 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ef5fcca..ee1faa8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,10 @@ pub mod types; pub mod utils; use error::{DeciderError, ProverError, VerifierError}; -use protocol::phases::{batching, ood, pesat, proximity, twin_constraint}; +use protocol::phases::{ + batching::Batching, ood::Ood, pesat::Pesat, proximity::Proximity, + proximity::ProximityVerify, twin_constraint::TwinConstraint, ProverPhase, VerifierPhase, +}; pub trait BoolResult { fn ok_or_err(self, err: E) -> Result<(), E>; @@ -156,31 +159,32 @@ impl< } = acc_witness; // Phase 2: PESAT — emit oracles (codewords), commit, squeeze τs. - let pesat = pesat::prove::( - prover_state, - &self.params.code, - &self.params.mt_leaf_hash_params, - &self.params.mt_two_to_one_hash_params, - &witnesses, + let pesat = Pesat:: { + code: &self.params.code, + mt_leaf_hash_params: &self.params.mt_leaf_hash_params, + mt_two_to_one_hash_params: &self.params.mt_two_to_one_hash_params, + witnesses: &witnesses, l1, log_m, - )?; + _phantom: PhantomData, + } + .prove(prover_state)?; // Phase 3a: twin-constraint sumcheck. - let tc = twin_constraint::prove::( - prover_state, - &pesat.codewords, - pesat.taus, + let tc = TwinConstraint:: { + fresh_codewords: &pesat.codewords, + fresh_taus: pesat.taus, acc_instance, - &acc_fs, - &acc_ws, - &instances, - &witnesses, - self.params.p.constraints(), + acc_witness_f: &acc_fs, + acc_witness_w: &acc_ws, + instances: &instances, + witnesses: &witnesses, + r1cs: self.params.p.constraints(), log_l, log_m, log_n, - ); + } + .prove(prover_state)?; // Phase 3b: bundled η, ν₀, new commitment, absorb — the "emit new // oracle + claims" step between twin-constraint and OOD. @@ -216,7 +220,12 @@ impl< prover_state.prover_message(&nu_0); // Phase 3c: OOD — point queries on the oracle. - let ood_out = ood::prove::(prover_state, &tc.f, self.params.config.s, log_n); + let ood_out = Ood { + oracle: &tc.f, + s: self.params.config.s, + log_n, + } + .prove(prover_state)?; // Sample shift queries — ordering-coupled to the batching ξ below. let queries = QueryIndices::::sample(prover_state, log_n, self.params.config.t); @@ -229,20 +238,26 @@ impl< zetas.extend(ood_chunks); zetas.extend(queries.evaluation_points.iter().map(|v| v.as_slice())); - let batching_out = batching::prove::( - prover_state, - &tc.f, - &zetas, - self.params.config.s, - self.params.config.t, + let batching_out = Batching { + oracle: &tc.f, + zetas_prefix: &zetas, + s: self.params.config.s, + t: self.params.config.t, log_n, - ); + } + .prove(prover_state)?; // Phase 3e: proximity — index queries + auth paths on accumulated + // fresh oracles (NOT on the reduced `f`, which is this round's new // oracle). let all_codewords: Vec> = acc_fs.into_iter().chain(pesat.codewords).collect(); - let prox = proximity::prove::(&queries, &pesat.td_0, &acc_tds, &all_codewords)?; + let prox = Proximity:: { + queries: &queries, + td_0: &pesat.td_0, + acc_td: &acc_tds, + all_codewords: &all_codewords, + } + .prove(prover_state)?; // Assemble new accumulator state and proof. let mut nus = Vec::with_capacity(1 + self.params.config.s); @@ -372,18 +387,19 @@ impl< let queries: QueryIndices = QueryIndices::from_squeezed_bytes(&bytes_shift_queries, log_n, self.params.config.t); - proximity::verify::( - &queries, - &rt_0, - &l2_roots, - &proof.auth_0, - &proof.auth_j, - &proof.shift_query_answers, - &self.params.mt_leaf_hash_params, - &self.params.mt_two_to_one_hash_params, + ProximityVerify:: { + queries: &queries, + rt_0: &rt_0, + l2_roots: &l2_roots, + auth_0: &proof.auth_0, + auth_j: &proof.auth_j, + shift_query_answers: &proof.shift_query_answers, + mt_leaf_hash_params: &self.params.mt_leaf_hash_params, + mt_two_to_one_hash_params: &self.params.mt_two_to_one_hash_params, l2, - self.params.config.t, - )?; + t: self.params.config.t, + } + .verify(verifier_state)?; // 8. Derive everything needed to state the batching claim. let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma_sumcheck); diff --git a/src/protocol/phases/batching.rs b/src/protocol/phases/batching.rs index 3e44be1..f48e886 100644 --- a/src/protocol/phases/batching.rs +++ b/src/protocol/phases/batching.rs @@ -22,7 +22,9 @@ use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState use std::collections::HashMap; use crate::count_ops; +use crate::error::ProverError; use crate::protocol::oracle::Oracle; +use crate::protocol::phases::ProverPhase; use crate::utils::poly::eq_poly; /// [CBBZ23] / HyperPlonk sparse-evaluation optimization: for shift-query @@ -76,74 +78,79 @@ pub struct BatchingOutput { pub mu: F, } -/// Run the batching sumcheck. +/// Batching sumcheck phase. /// /// `zetas_prefix` must contain `1 + s + t` evaluation points in the order /// `[ζ_0, ood_0, ..., ood_{s-1}, query_0, ..., query_{t-1}]`. -#[tracing::instrument( - name = "batching", - skip_all, - fields(s = s, t = t, log_n = log_n) -)] -pub fn prove( - prover_state: &mut ProverState, - oracle: &Oracle, - zetas_prefix: &[&[F]], - s: usize, - t: usize, - log_n: usize, -) -> BatchingOutput +pub struct Batching<'a, F: Field> { + pub oracle: &'a Oracle, + pub zetas_prefix: &'a [&'a [F]], + pub s: usize, + pub t: usize, + pub log_n: usize, +} + +impl<'a, F> ProverPhase for Batching<'a, F> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { - let n = oracle.len(); - let r = 1 + s + t; - let log_r = log2(r) as usize; - debug_assert_eq!(zetas_prefix.len(), r); + type Output = BatchingOutput; - let xis = prover_state.verifier_messages_vec::(log_r); + #[tracing::instrument( + name = "batching", + skip_all, + fields(s = self.s, t = self.t, log_n = self.log_n) + )] + fn prove(self, prover_state: &mut ProverState) -> Result { + let n = self.oracle.len(); + let r = 1 + self.s + self.t; + let log_r = log2(r) as usize; + debug_assert_eq!(self.zetas_prefix.len(), r); - // compute evaluations for xi and the dense ood_evals_vec for the first 1+s zetas - let (xi_eq_evals, ood_evals_vec) = { - let _s = tracing::info_span!("batching.eq_evals").entered(); - let xi_eq_evals = (0..r).map(|i| eq_poly(&xis, i)).collect::>(); - let ood_evals_vec = (0..1 + s) - .map(|i| { - (0..n) - .map(|a| eq_poly(zetas_prefix[i], a) * xi_eq_evals[i]) - .collect::>() - }) - .collect::>(); - (xi_eq_evals, ood_evals_vec) - }; + let xis = prover_state.verifier_messages_vec::(log_r); - // [CBBZ23] / HyperPlonk optimization for the t sparse shift-query zetas. - let id_non_0_eval_sums = { - let _s = tracing::info_span!("batching.accumulate_sparse").entered(); - accumulate_sparse_evaluations(zetas_prefix.to_vec(), xi_eq_evals, s, r) - }; + // compute evaluations for xi and the dense ood_evals_vec for the first 1+s zetas + let (xi_eq_evals, ood_evals_vec) = { + let _s = tracing::info_span!("batching.eq_evals").entered(); + let xi_eq_evals = (0..r).map(|i| eq_poly(&xis, i)).collect::>(); + let ood_evals_vec = (0..1 + self.s) + .map(|i| { + (0..n) + .map(|a| eq_poly(self.zetas_prefix[i], a) * xi_eq_evals[i]) + .collect::>() + }) + .collect::>(); + (xi_eq_evals, ood_evals_vec) + }; - // Run the inner-product sumcheck. `InnerProductProver` + `runner::sumcheck` - // is the new-style `SumcheckProver` entry point; wire format is three - // evaluations `[q(0), q(1), q(2)]` per round (`effsc::sumcheck_verify` - // reads them on the verifier side). The prover is MSB half-split, so the - // challenge vector arrives in MSB order — reverse once here so downstream - // MLE / eq_poly queries (arkworks' LSB-first convention) line up. - let alpha = { - let _s = tracing::info_span!("batching.sumcheck").entered(); - let log_n_bits = ark_std::log2(n) as u64; - count_ops!(BatchingRounds, log_n_bits); - let mut ip = InnerProductProver::new( - oracle.evals().to_vec(), - batched_constraint_poly(&ood_evals_vec, &id_non_0_eval_sums), - ); - let mut challenges = - sumcheck(&mut ip, log_n_bits as usize, prover_state, noop_hook).challenges; - challenges.reverse(); - challenges - }; + // [CBBZ23] / HyperPlonk optimization for the t sparse shift-query zetas. + let id_non_0_eval_sums = { + let _s = tracing::info_span!("batching.accumulate_sparse").entered(); + accumulate_sparse_evaluations(self.zetas_prefix.to_vec(), xi_eq_evals, self.s, r) + }; - let mu = oracle.query_at_point(&alpha); + // Run the inner-product sumcheck. `InnerProductProver` + `runner::sumcheck` + // is the new-style `SumcheckProver` entry point; wire format is three + // evaluations `[q(0), q(1), q(2)]` per round (`effsc::sumcheck_verify` + // reads them on the verifier side). The prover is MSB half-split, so the + // challenge vector arrives in MSB order — reverse once here so downstream + // MLE / eq_poly queries (arkworks' LSB-first convention) line up. + let alpha = { + let _s = tracing::info_span!("batching.sumcheck").entered(); + let log_n_bits = ark_std::log2(n) as u64; + count_ops!(BatchingRounds, log_n_bits); + let mut ip = InnerProductProver::new( + self.oracle.evals().to_vec(), + batched_constraint_poly(&ood_evals_vec, &id_non_0_eval_sums), + ); + let mut challenges = + sumcheck(&mut ip, log_n_bits as usize, prover_state, noop_hook).challenges; + challenges.reverse(); + challenges + }; - BatchingOutput { alpha, mu } + let mu = self.oracle.query_at_point(&alpha); + + Ok(BatchingOutput { alpha, mu }) + } } diff --git a/src/protocol/phases/mod.rs b/src/protocol/phases/mod.rs index 9eb1c4c..8d328f6 100644 --- a/src/protocol/phases/mod.rs +++ b/src/protocol/phases/mod.rs @@ -1,23 +1,57 @@ //! Warp IOR phases as first-class modules. //! -//! Paired spec: `docs/paper-mods/mod1_oracle.tex` (shared Oracle composition) -//! and the forthcoming `docs/paper-mods/mod2_structured_sumcheck.tex`, -//! `mod3_accumulator_state.tex`. +//! Paired spec: `docs/paper-mods/mod3_accumulator_state.tex` (forthcoming) — +//! the accumulator-as-state framing where each phase is a typed transition +//! in the protocol. //! -//! Each submodule implements one IOR from the Warp construction. The phase -//! functions are concrete (no `IOR` trait) but share a consistent shape: +//! Each submodule implements one IOR from the Warp construction. They share +//! a consistent shape, lifted into the [`ProverPhase`] / [`VerifierPhase`] +//! traits below: a phase is a struct that captures the static / borrowed +//! context it needs (codes, R1CS, merkle params, residues from upstream +//! phases), consumed by `prove` / `verify` along with a shared transcript +//! handle. //! -//! - **prove**: takes the current prover state, the accumulator, and any -//! fresh inputs; runs the IOR's prover; returns the reduced claim and any -//! emitted [`Oracle`](super::oracle::Oracle)s. -//! - **verify** (where applicable): consumes a subset of -//! `DerivedRandomness` plus the prior claim; returns the reduced claim. +//! The top-level orchestrators in `src/lib.rs::WARP::prove` and `::verify` +//! thread state between phases by chaining `Phase::prove` / `Phase::verify` +//! calls — each phase's typed [`Output`](ProverPhase::Output) feeds the next +//! phase's struct. //! -//! The top-level orchestrators live in `src/lib.rs::WARP::prove` and -//! `::verify`, which thread state between phases. +//! The traits cover what is genuinely uniform across phases (consume static +//! context + transcript → produce typed residue). Verifier-side phases like +//! "derive transcript randomness" that are pure plumbing are not wrapped in +//! `VerifierPhase` — they live in `src/protocol/transcript` and are called +//! directly from the orchestrator. pub mod batching; pub mod ood; pub mod pesat; pub mod proximity; pub mod twin_constraint; + +use spongefish::{ProverState, VerifierState}; + +use crate::error::{ProverError, VerifierError}; + +/// Prover-side IOR phase. +/// +/// Implementors are structs whose fields hold the borrowed context the phase +/// needs. `prove` consumes the phase struct together with the shared +/// `ProverState`, returns a typed [`Output`](Self::Output) residue threaded +/// forward to downstream phases. +pub trait ProverPhase { + type Output; + fn prove(self, prover_state: &mut ProverState) -> Result; +} + +/// Verifier-side IOR phase. +/// +/// Mirrors [`ProverPhase`] for the verifier. Phases whose verifier counterpart +/// is purely transcript-derived (PESAT, OOD) skip this trait — the orchestrator +/// reads the relevant randomness via helpers in `src/protocol/transcript`. +pub trait VerifierPhase { + type Output; + fn verify<'a>( + self, + verifier_state: &mut VerifierState<'a>, + ) -> Result; +} diff --git a/src/protocol/phases/ood.rs b/src/protocol/phases/ood.rs index 591cc94..7b56594 100644 --- a/src/protocol/phases/ood.rs +++ b/src/protocol/phases/ood.rs @@ -9,7 +9,9 @@ use ark_ff::{Field, PrimeField}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; use crate::count_ops; +use crate::error::ProverError; use crate::protocol::oracle::Oracle; +use crate::protocol::phases::ProverPhase; /// Output of the OOD phase: the flat challenge vector and the prover's /// answers at each chunked evaluation point. @@ -20,27 +22,32 @@ pub struct OodOutput { pub answers: Vec, } -/// Run the OOD phase: sample `s` evaluation points, query the oracle at -/// each, absorb the answers. -#[tracing::instrument(name = "ood", skip_all, fields(s = s, log_n = log_n))] -pub fn prove( - prover_state: &mut ProverState, - oracle: &Oracle, - s: usize, - log_n: usize, -) -> OodOutput +/// OOD phase: sample `s` evaluation points, query the oracle at each, absorb +/// the answers. +pub struct Ood<'a, F: Field> { + pub oracle: &'a Oracle, + pub s: usize, + pub log_n: usize, +} + +impl<'a, F> ProverPhase for Ood<'a, F> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { - let samples_flat = prover_state.verifier_messages_vec::(s * log_n); - count_ops!(OodPointQueries, s as u64); - let answers = samples_flat - .chunks(log_n) - .map(|zeta| oracle.query_at_point(zeta)) - .collect::>(); - prover_state.prover_messages(&answers); - OodOutput { - samples_flat, - answers, + type Output = OodOutput; + + #[tracing::instrument(name = "ood", skip_all, fields(s = self.s, log_n = self.log_n))] + fn prove(self, prover_state: &mut ProverState) -> Result { + let samples_flat = prover_state.verifier_messages_vec::(self.s * self.log_n); + count_ops!(OodPointQueries, self.s as u64); + let answers = samples_flat + .chunks(self.log_n) + .map(|zeta| self.oracle.query_at_point(zeta)) + .collect::>(); + prover_state.prover_messages(&answers); + Ok(OodOutput { + samples_flat, + answers, + }) } } diff --git a/src/protocol/phases/pesat.rs b/src/protocol/phases/pesat.rs index dad8f6f..0f43090 100644 --- a/src/protocol/phases/pesat.rs +++ b/src/protocol/phases/pesat.rs @@ -20,73 +20,86 @@ use ark_crypto_primitives::{ }; use ark_ff::{Field, PrimeField}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; +use std::marker::PhantomData; use crate::count_ops; use crate::crypto::merkle::build_codeword_leaves; use crate::error::ProverError; +use crate::protocol::phases::ProverPhase; use crate::types::PesatOutput; -/// Run the PESAT Reduction prover. -#[tracing::instrument( - name = "pesat", - skip_all, - fields(l1 = l1, log_m = log_m, n_witnesses = witnesses.len()) -)] -pub fn prove( - prover_state: &mut ProverState, - code: &C, - mt_leaf_hash_params: &::Parameters, - mt_two_to_one_hash_params: &::Parameters, - witnesses: &[Vec], - l1: usize, - log_m: usize, -) -> Result, ProverError> +/// PESAT phase: encode + commit + absorb + squeeze τ. +pub struct Pesat<'a, F, C, MT> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, C: LinearCode, MT: Config + From<[u8; 32]>>, { - // a. encode witnesses - let (codewords, leaves) = { - let _s = tracing::info_span!("pesat.encode").entered(); - count_ops!(EncodeCalls, witnesses.len() as u64); - build_codeword_leaves(code, witnesses, l1) - }; + pub code: &'a C, + pub mt_leaf_hash_params: &'a ::Parameters, + pub mt_two_to_one_hash_params: &'a ::Parameters, + pub witnesses: &'a [Vec], + pub l1: usize, + pub log_m: usize, + pub _phantom: PhantomData<(F, MT)>, +} + +impl<'a, F, C, MT> ProverPhase for Pesat<'a, F, C, MT> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + C: LinearCode, + MT: Config + From<[u8; 32]>>, +{ + type Output = PesatOutput; + + #[tracing::instrument( + name = "pesat", + skip_all, + fields(l1 = self.l1, log_m = self.log_m, n_witnesses = self.witnesses.len()) + )] + fn prove(self, prover_state: &mut ProverState) -> Result { + // a. encode witnesses + let (codewords, leaves) = { + let _s = tracing::info_span!("pesat.encode").entered(); + count_ops!(EncodeCalls, self.witnesses.len() as u64); + build_codeword_leaves(self.code, self.witnesses, self.l1) + }; - // b. evaluation claims - let mus = codewords.iter().map(|f| f[0]).collect::>(); + // b. evaluation claims + let mus = codewords.iter().map(|f| f[0]).collect::>(); - // c. commit to witnesses - let td_0 = { - let _s = tracing::info_span!("pesat.merkle_commit").entered(); - count_ops!(MerkleTreeBuilds); - MerkleTree::::new( - mt_leaf_hash_params, - mt_two_to_one_hash_params, - leaves.chunks_exact(l1).collect::>(), - )? - }; + // c. commit to witnesses + let td_0 = { + let _s = tracing::info_span!("pesat.merkle_commit").entered(); + count_ops!(MerkleTreeBuilds); + MerkleTree::::new( + self.mt_leaf_hash_params, + self.mt_two_to_one_hash_params, + leaves.chunks_exact(self.l1).collect::>(), + )? + }; - // d. absorb commitment and code evaluations; e/f. derive τ challenges. - let taus = { - let _s = tracing::info_span!("pesat.absorb_and_derive").entered(); - let root_bytes: [u8; 32] = td_0 - .root() - .as_ref() - .try_into() - .expect("root must be 32 bytes"); - prover_state.prover_message(&root_bytes); - prover_state.prover_messages(&mus); + // d. absorb commitment and code evaluations; e/f. derive τ challenges. + let taus = { + let _s = tracing::info_span!("pesat.absorb_and_derive").entered(); + let root_bytes: [u8; 32] = td_0 + .root() + .as_ref() + .try_into() + .expect("root must be 32 bytes"); + prover_state.prover_message(&root_bytes); + prover_state.prover_messages(&mus); - (0..l1) - .map(|_| prover_state.verifier_messages_vec::(log_m)) - .collect::>() - }; + (0..self.l1) + .map(|_| prover_state.verifier_messages_vec::(self.log_m)) + .collect::>() + }; - Ok(PesatOutput { - codewords, - td_0, - mus, - taus, - }) + Ok(PesatOutput { + codewords, + td_0, + mus, + taus, + }) + } } diff --git a/src/protocol/phases/proximity.rs b/src/protocol/phases/proximity.rs index d35ad75..88a9542 100644 --- a/src/protocol/phases/proximity.rs +++ b/src/protocol/phases/proximity.rs @@ -7,18 +7,22 @@ //! //! The query indices themselves are sampled from the transcript **before** //! this phase — see the orchestrator in `src/lib.rs` — so the batching -//! sumcheck can consume the same indices. +//! sumcheck can consume the same indices. Proximity itself does not interact +//! with the transcript: it produces proof artifacts (auth paths + answers), +//! which are then attached to the proof on the prover side and verified +//! against transcript-derived commitments on the verifier side. use ark_crypto_primitives::{ crh::{CRHScheme, TwoToOneCRHScheme}, merkle_tree::{Config, MerkleTree, Path}, - Error, }; use ark_ff::Field; +use spongefish::{ProverState, VerifierState}; use crate::count_ops; use crate::crypto::merkle::compute_auth_paths; -use crate::error::VerifierError; +use crate::error::{ProverError, VerifierError}; +use crate::protocol::phases::{ProverPhase, VerifierPhase}; use crate::protocol::query::QueryIndices; use crate::BoolResult; @@ -28,128 +32,149 @@ pub struct ProximityOutput { pub shift_query_answers: Vec>, } -/// Open the proximity queries: generate auth paths for the fresh commitment -/// and each accumulated commitment, and collect the codeword values at every -/// queried leaf. +/// Proximity prover: open the queries against the fresh and accumulated +/// commitments, collect codeword values at each queried leaf. /// /// `all_codewords` must list the **accumulated** codewords first, then the /// **fresh** PESAT codewords, matching the verifier's expectation at /// `lib.rs::verify` (the `[l2..]` slice is the fresh chunk). -#[tracing::instrument( - name = "proximity", - skip_all, - fields( - n_queries = queries.leaf_positions.len(), - n_accumulators = acc_td.len(), - n_codewords = all_codewords.len(), - ) -)] -pub fn prove( - queries: &QueryIndices, - td_0: &MerkleTree, - acc_td: &[MerkleTree], - all_codewords: &[Vec], -) -> Result, Error> +pub struct Proximity<'a, F: Field, MT: Config> { + pub queries: &'a QueryIndices, + pub td_0: &'a MerkleTree, + pub acc_td: &'a [MerkleTree], + pub all_codewords: &'a [Vec], +} + +impl<'a, F, MT> ProverPhase for Proximity<'a, F, MT> where F: Field, MT: Config, { - let auth_0 = { - let _s = tracing::info_span!("proximity.auth_0").entered(); - count_ops!(MerklePathsGenerated, queries.leaf_positions.len() as u64); - compute_auth_paths(td_0, &queries.leaf_positions)? - }; - - let auth_j = { - let _s = tracing::info_span!("proximity.auth_j").entered(); - count_ops!( - MerklePathsGenerated, - (acc_td.len() * queries.leaf_positions.len()) as u64 - ); - acc_td - .iter() - .map(|td| compute_auth_paths(td, &queries.leaf_positions)) - .collect::>>, Error>>()? - }; - - let shift_query_answers = { - let _s = tracing::info_span!("proximity.shift_queries").entered(); - let mut answers = - vec![vec![F::default(); all_codewords.len()]; queries.leaf_positions.len()]; - for (i, idx) in queries.leaf_positions.iter().enumerate() { - let row = all_codewords.iter().map(|f| f[*idx]).collect::>(); - answers[i] = row; - } - answers - }; - - Ok(ProximityOutput { - auth_0, - auth_j, - shift_query_answers, - }) + type Output = ProximityOutput; + + #[tracing::instrument( + name = "proximity", + skip_all, + fields( + n_queries = self.queries.leaf_positions.len(), + n_accumulators = self.acc_td.len(), + n_codewords = self.all_codewords.len(), + ) + )] + fn prove(self, _prover_state: &mut ProverState) -> Result { + let auth_0 = { + let _s = tracing::info_span!("proximity.auth_0").entered(); + count_ops!(MerklePathsGenerated, self.queries.leaf_positions.len() as u64); + compute_auth_paths(self.td_0, &self.queries.leaf_positions)? + }; + + let auth_j = { + let _s = tracing::info_span!("proximity.auth_j").entered(); + count_ops!( + MerklePathsGenerated, + (self.acc_td.len() * self.queries.leaf_positions.len()) as u64 + ); + self.acc_td + .iter() + .map(|td| compute_auth_paths(td, &self.queries.leaf_positions)) + .collect::>>, _>>()? + }; + + let shift_query_answers = { + let _s = tracing::info_span!("proximity.shift_queries").entered(); + let mut answers = vec![ + vec![F::default(); self.all_codewords.len()]; + self.queries.leaf_positions.len() + ]; + for (i, idx) in self.queries.leaf_positions.iter().enumerate() { + let row = self + .all_codewords + .iter() + .map(|f| f[*idx]) + .collect::>(); + answers[i] = row; + } + answers + }; + + Ok(ProximityOutput { + auth_0, + auth_j, + shift_query_answers, + }) + } } -/// Verify the proximity (shift-query) openings. +/// Proximity verifier: validate the auth paths against the rt_0 / l2_roots +/// commitments and check that the leaf claims line up. /// /// - `auth_0` opens the fresh PESAT tree at each query; expected leaves are /// the `l2..` slice of each `shift_query_answers` row (the fresh chunk). /// - `auth_j` opens each of `l2` accumulated trees at each query; expected /// leaf for accumulator `i` at query `j` is `shift_query_answers[j][i]`. -#[allow(clippy::too_many_arguments)] -#[tracing::instrument( - name = "proximity.verify", - skip_all, - fields(t = t, l2 = l2) -)] -pub fn verify( - queries: &QueryIndices, - rt_0: &MT::InnerDigest, - l2_roots: &[MT::InnerDigest], - auth_0: &[Path], - auth_j: &[Vec>], - shift_query_answers: &[Vec], - mt_leaf_hash_params: &::Parameters, - mt_two_to_one_hash_params: &::Parameters, - l2: usize, - t: usize, -) -> Result<(), VerifierError> +pub struct ProximityVerify<'a, F: Field, MT: Config> { + pub queries: &'a QueryIndices, + pub rt_0: &'a MT::InnerDigest, + pub l2_roots: &'a [MT::InnerDigest], + pub auth_0: &'a [Path], + pub auth_j: &'a [Vec>], + pub shift_query_answers: &'a [Vec], + pub mt_leaf_hash_params: &'a ::Parameters, + pub mt_two_to_one_hash_params: &'a ::Parameters, + pub l2: usize, + pub t: usize, +} + +impl<'a, F, MT> VerifierPhase for ProximityVerify<'a, F, MT> where F: Field, MT: Config, { - (shift_query_answers.len() == t).ok_or_err(VerifierError::NumShiftQueries)?; - - for (i, path) in auth_0.iter().enumerate() { - (path.leaf_index == queries.leaf_positions[i]).ok_or_err(VerifierError::ShiftQueryIndex)?; - - count_ops!(MerklePathsVerified); - let is_valid = path.verify( - mt_leaf_hash_params, - mt_two_to_one_hash_params, - rt_0, - &shift_query_answers[i][l2..], // leaves are evaluations of the l1 codewords - )?; - is_valid.ok_or_err(VerifierError::ShiftQuery)?; - } + type Output = (); - (auth_j.len() == l2).ok_or_err(VerifierError::NumL2Instances)?; - for (i, paths) in auth_j.iter().enumerate() { - (paths.len() == t).ok_or_err(VerifierError::NumShiftQueries)?; - let root = &l2_roots[i]; - for (j, path) in paths.iter().enumerate() { - (path.leaf_index == queries.leaf_positions[j]) + #[tracing::instrument( + name = "proximity.verify", + skip_all, + fields(t = self.t, l2 = self.l2) + )] + fn verify<'b>( + self, + _verifier_state: &mut VerifierState<'b>, + ) -> Result { + (self.shift_query_answers.len() == self.t).ok_or_err(VerifierError::NumShiftQueries)?; + + for (i, path) in self.auth_0.iter().enumerate() { + (path.leaf_index == self.queries.leaf_positions[i]) .ok_or_err(VerifierError::ShiftQueryIndex)?; + count_ops!(MerklePathsVerified); let is_valid = path.verify( - mt_leaf_hash_params, - mt_two_to_one_hash_params, - root, - [shift_query_answers[j][i]], + self.mt_leaf_hash_params, + self.mt_two_to_one_hash_params, + self.rt_0, + &self.shift_query_answers[i][self.l2..], // leaves are evaluations of the l1 codewords )?; is_valid.ok_or_err(VerifierError::ShiftQuery)?; } - } - Ok(()) + (self.auth_j.len() == self.l2).ok_or_err(VerifierError::NumL2Instances)?; + for (i, paths) in self.auth_j.iter().enumerate() { + (paths.len() == self.t).ok_or_err(VerifierError::NumShiftQueries)?; + let root = &self.l2_roots[i]; + for (j, path) in paths.iter().enumerate() { + (path.leaf_index == self.queries.leaf_positions[j]) + .ok_or_err(VerifierError::ShiftQueryIndex)?; + count_ops!(MerklePathsVerified); + let is_valid = path.verify( + self.mt_leaf_hash_params, + self.mt_two_to_one_hash_params, + root, + [self.shift_query_answers[j][i]], + )?; + is_valid.ok_or_err(VerifierError::ShiftQuery)?; + } + } + + Ok(()) + } } diff --git a/src/protocol/phases/twin_constraint.rs b/src/protocol/phases/twin_constraint.rs index b190ea4..7f05f0f 100644 --- a/src/protocol/phases/twin_constraint.rs +++ b/src/protocol/phases/twin_constraint.rs @@ -26,7 +26,9 @@ use effsc::{ use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; use crate::count_ops; +use crate::error::ProverError; use crate::protocol::oracle::Oracle; +use crate::protocol::phases::ProverPhase; use crate::relations::r1cs::R1CSConstraints; use crate::types::AccumulatorInstance; use crate::utils::{concat_slices, poly::eq_poly}; @@ -161,99 +163,121 @@ pub struct TwinConstraintOutput { pub beta_tau: Vec, } -/// Run the twin-constraint sumcheck prover. +/// Twin-constraint sumcheck phase: fold τ-zero-check + α-codeword check + +/// β-R1CS check into a single coefficient-form sumcheck. /// /// Takes codeword slices by reference so the caller (orchestrator) can hand -/// them to [`proximity::prove`](super::proximity::prove) after this phase +/// them to [`Proximity`](super::proximity::Proximity) after this phase /// returns. `fresh_taus` and `acc_instance` are consumed into the sumcheck /// tables — neither is needed downstream. -#[allow(clippy::too_many_arguments)] -#[tracing::instrument( - name = "twin_constraint", - skip_all, - fields(log_l = log_l, log_m = log_m, log_n = log_n) -)] -pub fn prove( - prover_state: &mut ProverState, - fresh_codewords: &[Vec], - fresh_taus: Vec>, - acc_instance: AccumulatorInstance, - acc_witness_f: &[Vec], - acc_witness_w: &[Vec], - instances: &[Vec], - witnesses: &[Vec], - r1cs: &R1CSConstraints, - log_l: usize, - log_m: usize, - log_n: usize, -) -> TwinConstraintOutput +pub struct TwinConstraint<'a, F, MT> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, MT: Config + From<[u8; 32]>>, { - let l1 = fresh_codewords.len(); + pub fresh_codewords: &'a [Vec], + pub fresh_taus: Vec>, + pub acc_instance: AccumulatorInstance, + pub acc_witness_f: &'a [Vec], + pub acc_witness_w: &'a [Vec], + pub instances: &'a [Vec], + pub witnesses: &'a [Vec], + pub r1cs: &'a R1CSConstraints, + pub log_l: usize, + pub log_m: usize, + pub log_n: usize, +} + +impl<'a, F, MT> ProverPhase for TwinConstraint<'a, F, MT> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + MT: Config + From<[u8; 32]>>, +{ + type Output = TwinConstraintOutput; - // a. zero-check randomness - let omega: F = prover_state.verifier_message(); - let tau = prover_state.verifier_messages_vec::(log_l); + #[tracing::instrument( + name = "twin_constraint", + skip_all, + fields(log_l = self.log_l, log_m = self.log_m, log_n = self.log_n) + )] + fn prove(self, prover_state: &mut ProverState) -> Result { + let TwinConstraint { + fresh_codewords, + fresh_taus, + acc_instance, + acc_witness_f, + acc_witness_w, + instances, + witnesses, + r1cs, + log_l, + log_m, + log_n, + } = self; + let l1 = fresh_codewords.len(); - // b. assemble sumcheck tables - let tau_eq_evals = Ascending::new(log_l) - .map(|p| eq_poly(&tau, p.index)) - .collect::>(); + // a. zero-check randomness + let omega: F = prover_state.verifier_message(); + let tau = prover_state.verifier_messages_vec::(log_l); - let alpha_vecs = concat_slices(&acc_instance.alpha, &vec![vec![F::zero(); log_n]; l1]); + // b. assemble sumcheck tables + let tau_eq_evals = Ascending::new(log_l) + .map(|p| eq_poly(&tau, p.index)) + .collect::>(); - let z_vecs: Vec> = acc_instance - .beta - .1 - .iter() - .zip(acc_witness_w) - .chain(instances.iter().zip(witnesses)) - .map(|(x, w)| concat_slices(x, w)) - .collect(); + let alpha_vecs = concat_slices(&acc_instance.alpha, &vec![vec![F::zero(); log_n]; l1]); - let beta_vecs: Vec> = acc_instance.beta.0.into_iter().chain(fresh_taus).collect(); + let z_vecs: Vec> = acc_instance + .beta + .1 + .iter() + .zip(acc_witness_w) + .chain(instances.iter().zip(witnesses)) + .map(|(x, w)| concat_slices(x, w)) + .collect(); - let tablewise = vec![ - concat_slices(acc_witness_f, fresh_codewords), // u - z_vecs, // z - alpha_vecs, // a - beta_vecs, // b - ]; - let pw = vec![tau_eq_evals]; // tau + let beta_vecs: Vec> = acc_instance.beta.0.into_iter().chain(fresh_taus).collect(); - let degree = 1 + (log_n + 1).max(log_m + 2); - let evaluator = TwinConstraintEvaluator { - r1cs, - omega, - degree, - }; + let tablewise = vec![ + concat_slices(acc_witness_f, fresh_codewords), // u + z_vecs, // z + alpha_vecs, // a + beta_vecs, // b + ]; + let pw = vec![tau_eq_evals]; // tau - // c. run the sumcheck. `CoefficientProverLSB` + `runner::sumcheck` is - // the new-style `SumcheckProver` entry point; wire format is `d+1` - // evaluations per round (the verifier uses `effsc::sumcheck_verify` on - // the other side). - let mut cc = CoefficientProverLSB::new(&evaluator, tablewise, pw); - { - let _s = tracing::info_span!("twin_constraint.sumcheck").entered(); - count_ops!(TwinConstraintRounds, log_l as u64); - let proof = sumcheck(&mut cc, log_l, prover_state, noop_hook); - debug_assert_eq!(proof.challenges.len(), log_l); - } + let degree = 1 + (log_n + 1).max(log_m + 2); + let evaluator = TwinConstraintEvaluator { + r1cs, + omega, + degree, + }; + + // c. run the sumcheck. `CoefficientProverLSB` + `runner::sumcheck` is + // the new-style `SumcheckProver` entry point; wire format is `d+1` + // evaluations per round (the verifier uses `effsc::sumcheck_verify` on + // the other side). + let mut cc = CoefficientProverLSB::new(&evaluator, tablewise, pw); + { + let _s = tracing::info_span!("twin_constraint.sumcheck").entered(); + count_ops!(TwinConstraintRounds, log_l as u64); + let proof = sumcheck(&mut cc, log_l, prover_state, noop_hook); + debug_assert_eq!(proof.challenges.len(), log_l); + } - // d. pull the single remaining row out of each tablewise table. - let reduced = cc.tablewise(); - debug_assert!(reduced.iter().all(|t| t.len() == 1)); - let f = reduced[0][0].clone(); - let z = reduced[1][0].clone(); - let zeta_0 = reduced[2][0].clone(); - let beta_tau = reduced[3][0].clone(); + // d. pull the single remaining row out of each tablewise table. + let reduced = cc.tablewise(); + debug_assert!(reduced.iter().all(|t| t.len() == 1)); + let f = reduced[0][0].clone(); + let z = reduced[1][0].clone(); + let zeta_0 = reduced[2][0].clone(); + let beta_tau = reduced[3][0].clone(); - TwinConstraintOutput { - f: Oracle::from_evals(f), - z, - zeta_0, - beta_tau, + Ok(TwinConstraintOutput { + f: Oracle::from_evals(f), + z, + zeta_0, + beta_tau, + }) } } From d2a3893e7c0fb6bfebdf83b1237c8257cb680ebf Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Sun, 3 May 2026 20:12:25 +0200 Subject: [PATCH 23/68] more ior trait --- docs/paper-mods/slides/iors.aux | 53 + docs/paper-mods/slides/iors.log | 1855 ++++++++++++++++++++++++ docs/paper-mods/slides/iors.nav | 37 + docs/paper-mods/slides/iors.out | 0 docs/paper-mods/slides/iors.pdf | Bin 0 -> 128936 bytes docs/paper-mods/slides/iors.snm | 0 docs/paper-mods/slides/iors.tex | 343 +++++ docs/paper-mods/slides/iors.toc | 0 docs/paper-mods/slides/iors.vrb | 22 + src/lib.rs | 449 +++--- src/protocol/phases/batching.rs | 196 ++- src/protocol/phases/mod.rs | 96 +- src/protocol/phases/ood.rs | 116 +- src/protocol/phases/pesat.rs | 130 +- src/protocol/phases/proximity.rs | 179 ++- src/protocol/phases/twin_constraint.rs | 337 ++++- src/protocol/query.rs | 1 + texput.log | 20 + 18 files changed, 3326 insertions(+), 508 deletions(-) create mode 100644 docs/paper-mods/slides/iors.aux create mode 100644 docs/paper-mods/slides/iors.log create mode 100644 docs/paper-mods/slides/iors.nav create mode 100644 docs/paper-mods/slides/iors.out create mode 100644 docs/paper-mods/slides/iors.pdf create mode 100644 docs/paper-mods/slides/iors.snm create mode 100644 docs/paper-mods/slides/iors.tex create mode 100644 docs/paper-mods/slides/iors.toc create mode 100644 docs/paper-mods/slides/iors.vrb create mode 100644 texput.log diff --git a/docs/paper-mods/slides/iors.aux b/docs/paper-mods/slides/iors.aux new file mode 100644 index 0000000..f70d6f4 --- /dev/null +++ b/docs/paper-mods/slides/iors.aux @@ -0,0 +1,53 @@ +\relax +\providecommand\hyper@newdestlabel[2]{} +\providecommand\HyperFirstAtBeginDocument{\AtBeginDocument} +\HyperFirstAtBeginDocument{\ifx\hyper@anchor\@undefined +\global\let\oldnewlabel\newlabel +\gdef\newlabel#1#2{\newlabelxx{#1}#2} +\gdef\newlabelxx#1#2#3#4#5#6{\oldnewlabel{#1}{{#2}{#3}}} +\AtEndDocument{\ifx\hyper@anchor\@undefined +\let\newlabel\oldnewlabel +\fi} +\fi} +\global\let\hyper@last\relax +\gdef\HyperFirstAtBeginDocument#1{#1} +\providecommand\HyField@AuxAddToFields[1]{} +\providecommand\HyField@AuxAddToCoFields[2]{} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{1}{1/1}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {1}{1}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{2}{2/2}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {2}{2}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{3}{3/3}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {3}{3}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{4}{4/4}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {4}{4}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{5}{5/5}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {5}{5}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{6}{6/6}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {6}{6}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{7}{7/7}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {7}{7}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{8}{8/8}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {8}{8}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{9}{9/9}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {9}{9}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{10}{10/10}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {10}{10}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{11}{11/11}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {11}{11}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{12}{12/12}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {12}{12}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{13}{13/13}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {13}{13}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{14}{14/14}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {14}{14}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{15}{15/15}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {15}{15}}} +\@writefile{nav}{\headcommand {\slideentry {0}{0}{16}{16/16}{}{0}}} +\@writefile{nav}{\headcommand {\beamer@framepages {16}{16}}} +\@writefile{nav}{\headcommand {\beamer@partpages {1}{16}}} +\@writefile{nav}{\headcommand {\beamer@subsectionpages {1}{16}}} +\@writefile{nav}{\headcommand {\beamer@sectionpages {1}{16}}} +\@writefile{nav}{\headcommand {\beamer@documentpages {16}}} +\@writefile{nav}{\headcommand {\gdef \inserttotalframenumber {15}}} +\gdef \@abspage@last{16} diff --git a/docs/paper-mods/slides/iors.log b/docs/paper-mods/slides/iors.log new file mode 100644 index 0000000..e2215ff --- /dev/null +++ b/docs/paper-mods/slides/iors.log @@ -0,0 +1,1855 @@ +This is LuaHBTeX, Version 1.16.0 (TeX Live 2023) (format=lualatex 2024.1.16) 3 MAY 2026 20:04 + restricted system commands enabled. +**iors.tex +(./iors.tex +LaTeX2e <2022-11-01> patch level 1 +Lua module: luaotfload 2022-10-03 3.23 Lua based OpenType font support +Lua module: lualibs 2022-10-04 2.75 ConTeXt Lua standard libraries. +Lua module: lualibs-extended 2022-10-04 2.75 ConTeXt Lua libraries -- extended c +ollection. +luaotfload | conf : Root cache directory is "/Users/zitek/Library/texlive/2023/t +exmf-var/luatex-cache/generic/names". +luaotfload | init : Loading fontloader "fontloader-2022-10-03.lua" from kpse-res +olved path "/usr/local/texlive/2023/texmf-dist/tex/luatex/luaotfload/fontloader- +2022-10-03.lua". +Lua-only attribute luaotfload@noligature = 1 +luaotfload | init : Context OpenType loader version 3.120 +Inserting `luaotfload.node_processor' in `pre_linebreak_filter'. +Inserting `luaotfload.node_processor' in `hpack_filter'. +Inserting `luaotfload.glyph_stream' in `glyph_stream_provider'. +Inserting `luaotfload.define_font' in `define_font'. +Lua-only attribute luaotfload_color_attribute = 2 +luaotfload | conf : Root cache directory is "/Users/zitek/Library/texlive/2023/t +exmf-var/luatex-cache/generic/names". +Inserting `luaotfload.harf.strip_prefix' in `find_opentype_file'. +Inserting `luaotfload.harf.strip_prefix' in `find_truetype_file'. +Removing `luaotfload.glyph_stream' from `glyph_stream_provider'. +Inserting `luaotfload.harf.glyphstream' in `glyph_stream_provider'. +Inserting `luaotfload.harf.finalize_vlist' in `post_linebreak_filter'. +Inserting `luaotfload.harf.finalize_hlist' in `hpack_filter'. +Inserting `luaotfload.cleanup_files' in `wrapup_run'. +Inserting `luaotfload.harf.finalize_unicode' in `finish_pdffile'. +Inserting `luaotfload.glyphinfo' in `glyph_info'. +Lua-only attribute luaotfload.letterspace_done = 3 +Inserting `luaotfload.aux.set_sscale_dimens' in `luaotfload.patch_font'. +Inserting `luaotfload.aux.set_font_index' in `luaotfload.patch_font'. +Inserting `luaotfload.aux.patch_cambria_domh' in `luaotfload.patch_font'. +Inserting `luaotfload.aux.fixup_fontdata' in `luaotfload.patch_font_unsafe'. +Inserting `luaotfload.aux.set_capheight' in `luaotfload.patch_font'. +Inserting `luaotfload.aux.set_xheight' in `luaotfload.patch_font'. +Inserting `luaotfload.rewrite_fontname' in `luaotfload.patch_font'. L3 programm +ing layer <2023-02-22> +Inserting `tracingstacklevels' in `input_level_string'. +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamer.cls +Document Class: beamer 2023/02/20 v3.69 A class for typesetting presentations +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasemodes.sty +(/usr/local/texlive/2023/texmf-dist/tex/latex/etoolbox/etoolbox.sty +Package: etoolbox 2020/10/05 v2.5k e-TeX tools for LaTeX (JAW) +\etb@tempcnta=\count183 +) +\beamer@tempbox=\box51 +\beamer@tempcount=\count184 +\c@beamerpauses=\count185 + +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasedecode.sty +\beamer@slideinframe=\count186 +\beamer@minimum=\count187 +\beamer@decode@box=\box52 +) +\beamer@commentbox=\box53 +\beamer@modecount=\count188 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty +Package: iftex 2022/02/03 v1.0f TeX engine tests +) +\headdp=\dimen139 +\footheight=\dimen140 +\sidebarheight=\dimen141 +\beamer@tempdim=\dimen142 +\beamer@finalheight=\dimen143 +\beamer@animht=\dimen144 +\beamer@animdp=\dimen145 +\beamer@animwd=\dimen146 +\beamer@leftmargin=\dimen147 +\beamer@rightmargin=\dimen148 +\beamer@leftsidebar=\dimen149 +\beamer@rightsidebar=\dimen150 +\beamer@boxsize=\dimen151 +\beamer@vboxoffset=\dimen152 +\beamer@descdefault=\dimen153 +\beamer@descriptionwidth=\dimen154 +\beamer@lastskip=\skip48 +\beamer@areabox=\box54 +\beamer@animcurrent=\box55 +\beamer@animshowbox=\box56 +\beamer@sectionbox=\box57 +\beamer@logobox=\box58 +\beamer@linebox=\box59 +\beamer@sectioncount=\count189 +\beamer@subsubsectionmax=\count190 +\beamer@subsectionmax=\count191 +\beamer@sectionmax=\count192 +\beamer@totalheads=\count193 +\beamer@headcounter=\count194 +\beamer@partstartpage=\count195 +\beamer@sectionstartpage=\count196 +\beamer@subsectionstartpage=\count197 +\beamer@animationtempa=\count198 +\beamer@animationtempb=\count199 +\beamer@xpos=\count266 +\beamer@ypos=\count267 +\beamer@ypos@offset=\count268 +\beamer@showpartnumber=\count269 +\beamer@currentsubsection=\count270 +\beamer@coveringdepth=\count271 +\beamer@sectionadjust=\count272 +\beamer@toclastsection=\count273 +\beamer@tocsectionnumber=\count274 + +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseoptions.sty +(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics/keyval.sty +Package: keyval 2022/05/29 v1.15 key=value parser (DPC) +\KV@toks@=\toks16 +)) +\beamer@paperwidth=\skip49 +\beamer@paperheight=\skip50 + +(/usr/local/texlive/2023/texmf-dist/tex/latex/geometry/geometry.sty +Package: geometry 2020/01/02 v5.9 Page Geometry + +(/usr/local/texlive/2023/texmf-dist/tex/generic/iftex/ifvtex.sty +Package: ifvtex 2019/10/25 v1.7 ifvtex legacy package. Use iftex instead. +) +\Gm@cnth=\count275 +\Gm@cntv=\count276 +\c@Gm@tempcnt=\count277 +\Gm@bindingoffset=\dimen155 +\Gm@wd@mp=\dimen156 +\Gm@odd@mp=\dimen157 +\Gm@even@mp=\dimen158 +\Gm@layoutwidth=\dimen159 +\Gm@layoutheight=\dimen160 +\Gm@layouthoffset=\dimen161 +\Gm@layoutvoffset=\dimen162 +\Gm@dimlist=\toks17 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/math/pgfmath.sty +(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.te +x +\pgfutil@everybye=\toks18 +\pgfutil@tempdima=\dimen163 +\pgfutil@tempdimb=\dimen164 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def +\pgfutil@abb=\box60 +) (/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/pgf.revision.tex) +Package: pgfrcs 2023-01-15 v3.1.10 (3.1.10) +)) +(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex +\pgfkeys@pathtoks=\toks19 +\pgfkeys@temptoks=\toks20 + +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfil +tered.code.tex +\pgfkeys@tmptoks=\toks21 +))) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex +\pgf@x=\dimen165 +\pgf@xa=\dimen166 +\pgf@xb=\dimen167 +\pgf@xc=\dimen168 +\pgf@y=\dimen169 +\pgf@ya=\dimen170 +\pgf@yb=\dimen171 +\pgf@yc=\dimen172 +\c@pgf@counta=\count278 +\c@pgf@countb=\count279 +\c@pgf@countc=\count280 +\c@pgf@countd=\count281 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex +\pgfmath@dimen=\dimen173 +\pgfmath@count=\count282 +\pgfmath@box=\box61 +\pgfmath@toks=\toks22 +\pgfmath@stack@operand=\toks23 +\pgfmath@stack@operation=\toks24 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code. +tex) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic +.code.tex) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigo +nometric.code.tex) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.rando +m.code.tex) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.compa +rison.code.tex) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base. +code.tex) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round +.code.tex) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc. +code.tex) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integ +erarithmetics.code.tex) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex +\c@pgfmathroundto@lastzeros=\count283 +))) (/usr/local/texlive/2023/texmf-dist/tex/latex/base/size10.clo +File: size10.clo 2022/07/02 v1.4n Standard LaTeX file (size option) +luaotfload | db : Font names database loaded from /Users/zitek/Library/texlive/2 +023/texmf-var/luatex-cache/generic/names/luaotfload-names.luc.gz) +(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty +(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics/graphicx.sty +Package: graphicx 2021/09/16 v1.2d Enhanced LaTeX Graphics (DPC,SPQR) + +(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics/graphics.sty +Package: graphics 2022/03/10 v1.4e Standard LaTeX Graphics (DPC,SPQR) + +(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics/trig.sty +Package: trig 2021/08/11 v1.11 sin cos tan (DPC) +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics-cfg/graphics.cfg +File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration +) +Package graphics Info: Driver file: luatex.def on input line 107. + +(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics-def/luatex.def +File: luatex.def 2022/09/22 v1.2d Graphics/color driver for luatex +)) +\Gin@req@height=\dimen174 +\Gin@req@width=\dimen175 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex +Package: pgfsys 2023-01-15 v3.1.10 (3.1.10) +\pgf@x=\dimen176 +\pgf@y=\dimen177 +\pgf@xa=\dimen178 +\pgf@ya=\dimen179 +\pgf@xb=\dimen180 +\pgf@yb=\dimen181 +\pgf@xc=\dimen182 +\pgf@yc=\dimen183 +\pgf@xd=\dimen184 +\pgf@yd=\dimen185 +\w@pgf@writea=\write3 +\r@pgf@reada=\read2 +\c@pgf@counta=\count284 +\c@pgf@countb=\count285 +\c@pgf@countc=\count286 +\c@pgf@countd=\count287 +\t@pgf@toka=\toks25 +\t@pgf@tokb=\toks26 +\t@pgf@tokc=\toks27 +\pgf@sys@id@count=\count288 +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg +File: pgf.cfg 2023-01-15 v3.1.10 (3.1.10) +) +Driver file for pgf: pgfsys-luatex.def + +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-luatex.d +ef +File: pgfsys-luatex.def 2023-01-15 v3.1.10 (3.1.10) + +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-p +df.def +File: pgfsys-common-pdf.def 2023-01-15 v3.1.10 (3.1.10) +))) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath. +code.tex +File: pgfsyssoftpath.code.tex 2023-01-15 v3.1.10 (3.1.10) +\pgfsyssoftpath@smallbuffer@items=\count289 +\pgfsyssoftpath@bigbuffer@items=\count290 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol. +code.tex +File: pgfsysprotocol.code.tex 2023-01-15 v3.1.10 (3.1.10) +)) (/usr/local/texlive/2023/texmf-dist/tex/latex/xcolor/xcolor.sty +Package: xcolor 2022/06/12 v2.14 LaTeX color extensions (UK) + +(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics-cfg/color.cfg +File: color.cfg 2016/01/02 v1.6 sample color configuration +) +Package xcolor Info: Driver file: luatex.def on input line 227. + +(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics/mathcolor.ltx) +Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1353. +Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1357. +Package xcolor Info: Model `RGB' extended on input line 1369. +Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1371. +Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1372. +Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1373. +Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1374. +Package xcolor Info: Model `Gray' substituted by `gray' on input line 1375. +Package xcolor Info: Model `wave' substituted by `hsb' on input line 1376. +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex +Package: pgfcore 2023-01-15 v3.1.10 (3.1.10) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfint.code.tex) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.co +de.tex +File: pgfcorepoints.code.tex 2023-01-15 v3.1.10 (3.1.10) +\pgf@picminx=\dimen186 +\pgf@picmaxx=\dimen187 +\pgf@picminy=\dimen188 +\pgf@picmaxy=\dimen189 +\pgf@pathminx=\dimen190 +\pgf@pathmaxx=\dimen191 +\pgf@pathminy=\dimen192 +\pgf@pathmaxy=\dimen193 +\pgf@xx=\dimen194 +\pgf@xy=\dimen195 +\pgf@yx=\dimen196 +\pgf@yy=\dimen197 +\pgf@zx=\dimen198 +\pgf@zy=\dimen199 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconst +ruct.code.tex +File: pgfcorepathconstruct.code.tex 2023-01-15 v3.1.10 (3.1.10) +\pgf@path@lastx=\dimen256 +\pgf@path@lasty=\dimen257 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage +.code.tex +File: pgfcorepathusage.code.tex 2023-01-15 v3.1.10 (3.1.10) +\pgf@shorten@end@additional=\dimen258 +\pgf@shorten@start@additional=\dimen259 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.co +de.tex +File: pgfcorescopes.code.tex 2023-01-15 v3.1.10 (3.1.10) +\pgfpic=\box62 +\pgf@hbox=\box63 +\pgf@layerbox@main=\box64 +\pgf@picture@serial@count=\count291 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicst +ate.code.tex +File: pgfcoregraphicstate.code.tex 2023-01-15 v3.1.10 (3.1.10) +\pgflinewidth=\dimen260 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransform +ations.code.tex +File: pgfcoretransformations.code.tex 2023-01-15 v3.1.10 (3.1.10) +\pgf@pt@x=\dimen261 +\pgf@pt@y=\dimen262 +\pgf@pt@temp=\dimen263 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.cod +e.tex +File: pgfcorequick.code.tex 2023-01-15 v3.1.10 (3.1.10) +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.c +ode.tex +File: pgfcoreobjects.code.tex 2023-01-15 v3.1.10 (3.1.10) +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathproce +ssing.code.tex +File: pgfcorepathprocessing.code.tex 2023-01-15 v3.1.10 (3.1.10) +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.co +de.tex +File: pgfcorearrows.code.tex 2023-01-15 v3.1.10 (3.1.10) +\pgfarrowsep=\dimen264 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.cod +e.tex +File: pgfcoreshade.code.tex 2023-01-15 v3.1.10 (3.1.10) +\pgf@max=\dimen265 +\pgf@sys@shading@range@num=\count292 +\pgf@shadingcount=\count293 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.cod +e.tex +File: pgfcoreimage.code.tex 2023-01-15 v3.1.10 (3.1.10) +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal. +code.tex +File: pgfcoreexternal.code.tex 2023-01-15 v3.1.10 (3.1.10) +\pgfexternal@startupbox=\box65 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.co +de.tex +File: pgfcorelayers.code.tex 2023-01-15 v3.1.10 (3.1.10) +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretranspare +ncy.code.tex +File: pgfcoretransparency.code.tex 2023-01-15 v3.1.10 (3.1.10) +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns. +code.tex +File: pgfcorepatterns.code.tex 2023-01-15 v3.1.10 (3.1.10) +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code. +tex +File: pgfcorerdf.code.tex 2023-01-15 v3.1.10 (3.1.10) +))) (/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/utilities/xxcolor.sty +Package: xxcolor 2003/10/24 ver 0.1 +\XC@nummixins=\count294 +\XC@countmixins=\count295 +) (/usr/local/texlive/2023/texmf-dist/tex/latex/base/atbegshi-ltx.sty +Package: atbegshi-ltx 2021/01/10 v1.0c Emulation of the original atbegshi +package with kernel methods +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/hyperref/hyperref.sty +Package: hyperref 2023-02-07 v7.00v Hypertext links for LaTeX + +(/usr/local/texlive/2023/texmf-dist/tex/generic/ltxcmds/ltxcmds.sty +Package: ltxcmds 2020-05-10 v1.25 LaTeX kernel commands for general use (HO) +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pdftexcmds/pdftexcmds.sty +Package: pdftexcmds 2020-06-27 v0.33 Utility functions of pdfTeX for LuaTeX (HO +) + +(/usr/local/texlive/2023/texmf-dist/tex/generic/infwarerr/infwarerr.sty +Package: infwarerr 2019/12/03 v1.5 Providing info/warning/error messages (HO) +) +Package pdftexcmds Info: \pdf@primitive is available. +Package pdftexcmds Info: \pdf@ifprimitive is available. +Package pdftexcmds Info: \pdfdraftmode found. +\pdftexcmds@toks=\toks28 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/kvsetkeys/kvsetkeys.sty +Package: kvsetkeys 2022-10-05 v1.19 Key value parser (HO) +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/kvdefinekeys/kvdefinekeys.sty +Package: kvdefinekeys 2019-12-19 v1.6 Define keys (HO) +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pdfescape/pdfescape.sty +Package: pdfescape 2019/12/09 v1.15 Implements pdfTeX's escape features (HO) +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/hycolor/hycolor.sty +Package: hycolor 2020-01-27 v1.10 Color options for hyperref/bookmark (HO) +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/letltxmacro/letltxmacro.sty +Package: letltxmacro 2019/12/03 v1.6 Let assignment for LaTeX macros (HO) +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/auxhook/auxhook.sty +Package: auxhook 2019-12-17 v1.6 Hooks for auxiliary files (HO) +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/hyperref/nameref.sty +Package: nameref 2022-05-17 v2.50 Cross-referencing by name of section + +(/usr/local/texlive/2023/texmf-dist/tex/latex/refcount/refcount.sty +Package: refcount 2019/12/15 v3.6 Data extraction from label references (HO) +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/gettitlestring/gettitlestring.s +ty +Package: gettitlestring 2019/12/15 v1.6 Cleanup title references (HO) + (/usr/local/texlive/2023/texmf-dist/tex/latex/kvoptions/kvoptions.sty +Package: kvoptions 2022-06-15 v3.15 Key value format for package options (HO) +)) +\c@section@level=\count296 +) +\@linkdim=\dimen266 +\Hy@linkcounter=\count297 +\Hy@pagecounter=\count298 + +(/usr/local/texlive/2023/texmf-dist/tex/latex/hyperref/pd1enc.def +File: pd1enc.def 2023-02-07 v7.00v Hyperref: PDFDocEncoding definition (HO) +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/intcalc/intcalc.sty +Package: intcalc 2019/12/15 v1.3 Expandable calculations with integers (HO) +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/etexcmds/etexcmds.sty +Package: etexcmds 2019/12/15 v1.7 Avoid name clashes with e-TeX commands (HO) +) +\Hy@SavedSpaceFactor=\count299 + +(/usr/local/texlive/2023/texmf-dist/tex/latex/hyperref/puenc.def +File: puenc.def 2023-02-07 v7.00v Hyperref: PDF Unicode definition (HO) +) +Package hyperref Info: Option `bookmarks' set `true' on input line 4060. +Package hyperref Info: Option `bookmarksopen' set `true' on input line 4060. +Package hyperref Info: Option `implicit' set `false' on input line 4060. +Package hyperref Info: Hyper figures OFF on input line 4177. +Package hyperref Info: Link nesting OFF on input line 4182. +Package hyperref Info: Hyper index ON on input line 4185. +Package hyperref Info: Plain pages OFF on input line 4192. +Package hyperref Info: Backreferencing OFF on input line 4197. +Package hyperref Info: Implicit mode OFF; no redefinition of LaTeX internals. +Package hyperref Info: Bookmarks ON on input line 4425. +\c@Hy@tempcnt=\count300 + +(/usr/local/texlive/2023/texmf-dist/tex/latex/url/url.sty +\Urlmuskip=\muskip16 +Package: url 2013/09/16 ver 3.4 Verb mode for urls, etc. +) +LaTeX Info: Redefining \url on input line 4763. +\XeTeXLinkMargin=\dimen267 + +(/usr/local/texlive/2023/texmf-dist/tex/generic/bitset/bitset.sty +Package: bitset 2019/12/09 v1.3 Handle bit-vector datatype (HO) + +(/usr/local/texlive/2023/texmf-dist/tex/generic/bigintcalc/bigintcalc.sty +Package: bigintcalc 2019/12/15 v1.5 Expandable calculations on big integers (HO +) +)) +\Fld@menulength=\count301 +\Field@Width=\dimen268 +\Fld@charsize=\dimen269 +Package hyperref Info: Hyper figures OFF on input line 6042. +Package hyperref Info: Link nesting OFF on input line 6047. +Package hyperref Info: Hyper index ON on input line 6050. +Package hyperref Info: backreferencing OFF on input line 6057. +Package hyperref Info: Link coloring OFF on input line 6062. +Package hyperref Info: Link coloring with OCG OFF on input line 6067. +Package hyperref Info: PDF/A mode OFF on input line 6072. +\Hy@abspage=\count302 + + +Package hyperref Message: Stopped early. + +) +Package hyperref Info: Driver (autodetected): hluatex. + (/usr/local/texlive/2023/texmf-dist/tex/latex/hyperref/hluatex.def +File: hluatex.def 2023-02-07 v7.00v Hyperref driver for luaTeX + +(/usr/local/texlive/2023/texmf-dist/tex/generic/stringenc/stringenc.sty +Package: stringenc 2019/11/29 v1.12 Convert strings between diff. encodings (HO +) +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/base/atveryend-ltx.sty +Package: atveryend-ltx 2020/08/19 v1.0a Emulation of the original atveryend pac +kage +with kernel methods +) +\Fld@listcount=\count303 +\c@bookmark@seq@number=\count304 + +(/usr/local/texlive/2023/texmf-dist/tex/latex/rerunfilecheck/rerunfilecheck.sty +Package: rerunfilecheck 2022-07-10 v1.10 Rerun checks for auxiliary files (HO) + +(/usr/local/texlive/2023/texmf-dist/tex/generic/uniquecounter/uniquecounter.sty +Package: uniquecounter 2019/12/15 v1.4 Provide unlimited unique counter (HO) +) +Package uniquecounter Info: New unique counter `rerunfilecheck' on input line 2 +85. +)) (/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaserequires.sty +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasecompatibility.st +y) (/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasefont.sty +(/usr/local/texlive/2023/texmf-dist/tex/latex/amsfonts/amssymb.sty +Package: amssymb 2013/01/14 v3.01 AMS font symbols + +(/usr/local/texlive/2023/texmf-dist/tex/latex/amsfonts/amsfonts.sty +Package: amsfonts 2013/01/14 v3.01 Basic AMSFonts support +\@emptytoks=\toks29 +\symAMSa=\mathgroup4 +\symAMSb=\mathgroup5 +LaTeX Font Info: Redeclaring math symbol \hbar on input line 98. +LaTeX Font Info: Overwriting math alphabet `\mathfrak' in version `bold' +(Font) U/euf/m/n --> U/euf/b/n on input line 106. +)) +(/usr/local/texlive/2023/texmf-dist/tex/latex/sansmathaccent/sansmathaccent.sty +Package: sansmathaccent 2020/01/31 +(/usr/local/texlive/2023/texmf-dist/tex/latex/koma-script/scrlfile.sty +Package: scrlfile 2022/10/12 v3.38 KOMA-Script package (file load hooks) + +(/usr/local/texlive/2023/texmf-dist/tex/latex/koma-script/scrlfile-hook.sty +Package: scrlfile-hook 2022/10/12 v3.38 KOMA-Script package (using LaTeX hooks) + + +(/usr/local/texlive/2023/texmf-dist/tex/latex/koma-script/scrlogo.sty +Package: scrlogo 2022/10/12 v3.38 KOMA-Script package (logo) +))))) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasetranslator.sty +(/usr/local/texlive/2023/texmf-dist/tex/latex/translator/translator.sty +Package: translator 2021-05-31 v1.12d Easy translation of strings in LaTeX +)) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasemisc.sty) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasetwoscreens.sty) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseoverlay.sty +\beamer@argscount=\count305 +\beamer@lastskipcover=\skip51 +\beamer@trivlistdepth=\count306 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasetitle.sty) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasesection.sty +\c@lecture=\count307 +\c@part=\count308 +\c@section=\count309 +\c@subsection=\count310 +\c@subsubsection=\count311 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseframe.sty +\beamer@framebox=\box66 +\beamer@frametitlebox=\box67 +\beamer@zoombox=\box68 +\beamer@zoomcount=\count312 +\beamer@zoomframecount=\count313 +\beamer@frametextheight=\dimen270 +\c@subsectionslide=\count314 +\beamer@frametopskip=\skip52 +\beamer@framebottomskip=\skip53 +\beamer@frametopskipautobreak=\skip54 +\beamer@framebottomskipautobreak=\skip55 +\beamer@envbody=\toks30 +\framewidth=\dimen271 +\c@framenumber=\count315 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseverbatim.sty +\beamer@verbatimfileout=\write4 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseframesize.sty +\beamer@splitbox=\box69 +\beamer@autobreakcount=\count316 +\beamer@autobreaklastheight=\dimen272 +\beamer@frametitletoks=\toks31 +\beamer@framesubtitletoks=\toks32 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseframecomponents. +sty +\beamer@footins=\box70 +) (/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasecolor.sty) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasenotes.sty +\beamer@frameboxcopy=\box71 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasetoc.sty) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasetemplates.sty +\beamer@sbttoks=\toks33 + +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseauxtemplates.sty +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseboxes.sty +\bmb@box=\box72 +\bmb@colorbox=\box73 +\bmb@boxwidth=\dimen273 +\bmb@boxheight=\dimen274 +\bmb@prevheight=\dimen275 +\bmb@temp=\dimen276 +\bmb@dima=\dimen277 +\bmb@dimb=\dimen278 +\bmb@prevheight=\dimen279 +) +\beamer@blockheadheight=\dimen280 +)) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaselocalstructure.s +ty (/usr/local/texlive/2023/texmf-dist/tex/latex/tools/enumerate.sty +Package: enumerate 2015/07/23 v3.00 enumerate extensions (DPC) +\@enLab=\toks34 +) +\beamer@bibiconwidth=\skip56 +\c@figure=\count317 +\c@table=\count318 +\abovecaptionskip=\skip57 +\belowcaptionskip=\skip58 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasenavigation.sty +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasenavigationsymbol +s.tex) +\beamer@section@min@dim=\dimen281 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasetheorems.sty +(/usr/local/texlive/2023/texmf-dist/tex/latex/amsmath/amsmath.sty +Package: amsmath 2022/04/08 v2.17n AMS math features +\@mathmargin=\skip59 + +For additional information on amsmath, use the `?' option. +(/usr/local/texlive/2023/texmf-dist/tex/latex/amsmath/amstext.sty +Package: amstext 2021/08/26 v2.01 AMS text + +(/usr/local/texlive/2023/texmf-dist/tex/latex/amsmath/amsgen.sty +File: amsgen.sty 1999/11/30 v2.0 generic functions +\@emptytoks=\toks35 +\ex@=\dimen282 +)) +(/usr/local/texlive/2023/texmf-dist/tex/latex/amsmath/amsbsy.sty +Package: amsbsy 1999/11/29 v1.2d Bold Symbols +\pmbraise@=\dimen283 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/amsmath/amsopn.sty +Package: amsopn 2022/04/08 v2.04 operator names +) +\inf@bad=\count319 +LaTeX Info: Redefining \frac on input line 236. +\uproot@=\count320 +\leftroot@=\count321 +LaTeX Info: Redefining \overline on input line 399. +LaTeX Info: Redefining \colon on input line 410. +\classnum@=\count322 +\DOTSCASE@=\count323 +LaTeX Info: Redefining \ldots on input line 496. +LaTeX Info: Redefining \dots on input line 499. +LaTeX Info: Redefining \cdots on input line 620. +\Mathstrutbox@=\box74 +\strutbox@=\box75 +LaTeX Info: Redefining \big on input line 722. +LaTeX Info: Redefining \Big on input line 723. +LaTeX Info: Redefining \bigg on input line 724. +LaTeX Info: Redefining \Bigg on input line 725. +\big@size=\dimen284 +LaTeX Font Info: Redeclaring font encoding OML on input line 743. +LaTeX Font Info: Redeclaring font encoding OMS on input line 744. +\macc@depth=\count324 +LaTeX Info: Redefining \bmod on input line 905. +LaTeX Info: Redefining \pmod on input line 910. +LaTeX Info: Redefining \smash on input line 940. +LaTeX Info: Redefining \relbar on input line 970. +LaTeX Info: Redefining \Relbar on input line 971. +\c@MaxMatrixCols=\count325 +\dotsspace@=\muskip17 +\c@parentequation=\count326 +\dspbrk@lvl=\count327 +\tag@help=\toks36 +\row@=\count328 +\column@=\count329 +\maxfields@=\count330 +\andhelp@=\toks37 +\eqnshift@=\dimen285 +\alignsep@=\dimen286 +\tagshift@=\dimen287 +\tagwidth@=\dimen288 +\totwidth@=\dimen289 +\lineht@=\dimen290 +\@envbody=\toks38 +\multlinegap=\skip60 +\multlinetaggap=\skip61 +\mathdisplay@stack=\toks39 +LaTeX Info: Redefining \[ on input line 2953. +LaTeX Info: Redefining \] on input line 2954. +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/amscls/amsthm.sty +Package: amsthm 2020/05/29 v2.20.6 +\thm@style=\toks40 +\thm@bodyfont=\toks41 +\thm@headfont=\toks42 +\thm@notefont=\toks43 +\thm@headpunct=\toks44 +\thm@preskip=\skip62 +\thm@postskip=\skip63 +\thm@headsep=\skip64 +\dth@everypar=\toks45 +) +\c@theorem=\count331 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasethemes.sty)) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerthemedefault.sty +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerfontthemedefault.sty +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamercolorthemedefault.st +y) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerinnerthemedefault.st +y +\beamer@dima=\dimen291 +\beamer@dimb=\dimen292 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerouterthemedefault.st +y))) (/usr/local/texlive/2023/texmf-dist/tex/latex/listings/listings.sty +\lst@mode=\count332 +\lst@gtempboxa=\box76 +\lst@token=\toks46 +\lst@length=\count333 +\lst@currlwidth=\dimen293 +\lst@column=\count334 +\lst@pos=\count335 +\lst@lostspace=\dimen294 +\lst@width=\dimen295 +\lst@newlines=\count336 +\lst@lineno=\count337 +\lst@maxwidth=\dimen296 + +(/usr/local/texlive/2023/texmf-dist/tex/latex/listings/lstmisc.sty +File: lstmisc.sty 2023/02/27 1.9 (Carsten Heinz) +\c@lstnumber=\count338 +\lst@skipnumbers=\count339 +\lst@framebox=\box77 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/listings/listings.cfg +File: listings.cfg 2023/02/27 1.9 listings configuration +)) +Package: listings 2023/02/27 1.9 (Carsten Heinz) + +(/usr/local/texlive/2023/texmf-dist/tex/latex/booktabs/booktabs.sty +Package: booktabs 2020/01/12 v1.61803398 Publication quality tables +\heavyrulewidth=\dimen297 +\lightrulewidth=\dimen298 +\cmidrulewidth=\dimen299 +\belowrulesep=\dimen300 +\belowbottomsep=\dimen301 +\aboverulesep=\dimen302 +\abovetopsep=\dimen303 +\cmidrulesep=\dimen304 +\cmidrulekern=\dimen305 +\defaultaddspace=\dimen306 +\@cmidla=\count340 +\@cmidlb=\count341 +\@aboverulesep=\dimen307 +\@belowrulesep=\dimen308 +\@thisruleclass=\count342 +\@lastruleclass=\count343 +\@thisrulewidth=\dimen309 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/tools/array.sty +Package: array 2022/09/04 v2.5g Tabular extension package (FMi) +\col@sep=\dimen310 +\ar@mcellbox=\box78 +\extrarowheight=\dimen311 +\NC@list=\toks47 +\extratabsurround=\skip65 +\backup@length=\skip66 +\ar@cellbox=\box79 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamertheme-metropolis/beamerthem +emetropolis.sty +Package: beamerthememetropolis 2017/01/23 v1.2 Metropolis Beamer theme + +(/usr/local/texlive/2023/texmf-dist/tex/latex/pgfopts/pgfopts.sty +Package: pgfopts 2014/07/10 v2.1a LaTeX package options with pgfkeys +\pgfopts@list@add@a@toks=\toks48 +\pgfopts@list@add@b@toks=\toks49 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamertheme-metropolis/beamerinne +rthememetropolis.sty +Package: beamerinnerthememetropolis 2017/01/23 Metropolis inner theme + +(/usr/local/texlive/2023/texmf-dist/tex/latex/tools/calc.sty +Package: calc 2017/05/25 v4.3 Infix arithmetic (KKT,FJ) +\calc@Acount=\count344 +\calc@Bcount=\count345 +\calc@Adimen=\dimen312 +\calc@Bdimen=\dimen313 +\calc@Askip=\skip67 +\calc@Bskip=\skip68 +LaTeX Info: Redefining \setlength on input line 80. +LaTeX Info: Redefining \addtolength on input line 81. +\calc@Ccount=\count346 +\calc@Cskip=\skip69 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty +(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty +Package: pgf 2023-01-15 v3.1.10 (3.1.10) + +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.cod +e.tex +File: pgfmoduleshapes.code.tex 2023-01-15 v3.1.10 (3.1.10) +\pgfnodeparttextbox=\box80 +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code. +tex +File: pgfmoduleplot.code.tex 2023-01-15 v3.1.10 (3.1.10) +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version +-0-65.sty +Package: pgfcomp-version-0-65 2023-01-15 v3.1.10 (3.1.10) +\pgf@nodesepstart=\dimen314 +\pgf@nodesepend=\dimen315 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version +-1-18.sty +Package: pgfcomp-version-1-18 2023-01-15 v3.1.10 (3.1.10) +)) +(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/utilities/pgffor.sty +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex +Package: pgffor 2023-01-15 v3.1.10 (3.1.10) +\pgffor@iter=\dimen316 +\pgffor@skip=\dimen317 +\pgffor@stack=\toks50 +\pgffor@toks=\toks51 +)) +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.cod +e.tex +Package: tikz 2023-01-15 v3.1.10 (3.1.10) + +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothan +dlers.code.tex +File: pgflibraryplothandlers.code.tex 2023-01-15 v3.1.10 (3.1.10) +\pgf@plot@mark@count=\count347 +\pgfplotmarksize=\dimen318 +) +\tikz@lastx=\dimen319 +\tikz@lasty=\dimen320 +\tikz@lastxsaved=\dimen321 +\tikz@lastysaved=\dimen322 +\tikz@lastmovetox=\dimen323 +\tikz@lastmovetoy=\dimen324 +\tikzleveldistance=\dimen325 +\tikzsiblingdistance=\dimen326 +\tikz@figbox=\box81 +\tikz@figbox@bg=\box82 +\tikz@tempbox=\box83 +\tikz@tempbox@bg=\box84 +\tikztreelevel=\count348 +\tikznumberofchildren=\count349 +\tikznumberofcurrentchild=\count350 +\tikz@fig@count=\count351 + +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.cod +e.tex +File: pgfmodulematrix.code.tex 2023-01-15 v3.1.10 (3.1.10) +\pgfmatrixcurrentrow=\count352 +\pgfmatrixcurrentcolumn=\count353 +\pgf@matrix@numberofcolumns=\count354 +) +\tikz@expandcount=\count355 + +(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/frontendlayer/tikz/librarie +s/tikzlibrarytopaths.code.tex +File: tikzlibrarytopaths.code.tex 2023-01-15 v3.1.10 (3.1.10) +))) +\metropolis@titleseparator@linewidth=\skip70 +\metropolis@progressonsectionpage=\skip71 +\metropolis@progressonsectionpage@linewidth=\skip72 +\metropolis@blocksep=\skip73 +\metropolis@blockadjust=\skip74 +\metropolis@parskip=\skip75 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamertheme-metropolis/beameroute +rthememetropolis.sty +Package: beamerouterthememetropolis 2017/01/23 Metropolis outer theme +\metropolis@frametitle@padding=\skip76 +\metropolis@progressinheadfoot=\skip77 +\metropolis@progressinheadfoot@linewidth=\skip78 +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamertheme-metropolis/beamercolo +rthememetropolis.sty +Package: beamercolorthememetropolis 2017/01/23 Metropolis color theme +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/beamertheme-metropolis/beamerfont +thememetropolis.sty +Package: beamerfontthememetropolis 2017/01/23 Metropolis font theme + +(/usr/local/texlive/2023/texmf-dist/tex/generic/iftex/ifxetex.sty +Package: ifxetex 2019/10/25 v0.7 ifxetex legacy package. Use iftex instead. +) +(/usr/local/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty +Package: ifluatex 2019/10/25 v1.5 ifluatex legacy package. Use iftex instead. +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/fontspec/fontspec.sty +(/usr/local/texlive/2023/texmf-dist/tex/latex/l3packages/xparse/xparse.sty +(/usr/local/texlive/2023/texmf-dist/tex/latex/l3kernel/expl3.sty +Package: expl3 2023-02-22 L3 programming layer (loader) + +(/usr/local/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-luatex.def +File: l3backend-luatex.def 2023-01-16 L3 backend support: PDF output (LuaTeX) +\l__color_backend_stack_int=\count356 +\l__pdf_internal_box=\box85 +)) +Package: xparse 2023-02-02 L3 Experimental document command parser +) +Package: fontspec 2022/01/15 v2.8a Font selection for XeLaTeX and LuaLaTeX +Lua module: fontspec 2022/01/15 2.8a Font selection for XeLaTeX and LuaLaTeX (/ +usr/local/texlive/2023/texmf-dist/tex/latex/fontspec/fontspec-luatex.sty +Package: fontspec-luatex 2022/01/15 v2.8a Font selection for XeLaTeX and LuaLaT +eX +\l__fontspec_script_int=\count357 +\l__fontspec_language_int=\count358 +\l__fontspec_strnum_int=\count359 +\l__fontspec_tmp_int=\count360 +\l__fontspec_tmpa_int=\count361 +\l__fontspec_tmpb_int=\count362 +\l__fontspec_tmpc_int=\count363 +\l__fontspec_em_int=\count364 +\l__fontspec_emdef_int=\count365 +\l__fontspec_strong_int=\count366 +\l__fontspec_strongdef_int=\count367 +\l__fontspec_tmpa_dim=\dimen327 +\l__fontspec_tmpb_dim=\dimen328 +\l__fontspec_tmpc_dim=\dimen329 + +(/usr/local/texlive/2023/texmf-dist/tex/latex/base/fontenc.sty +Package: fontenc 2021/04/29 v2.0v Standard LaTeX package +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/fontspec/fontspec.cfg))) +\c@fontsnotfound=\count368 +luaotfload | cache : Lookup cache loaded from /Users/zitek/Library/texlive/2023/ +texmf-var/luatex-cache/generic/names/luaotfload-lookup-cache.luc. + +Package fontspec Info: Font family 'FiraSansLight(0)' created for font 'Fira +(fontspec) Sans Light' with options +(fontspec) [Ligatures=TeX,ItalicFont={Fira Sans Light +(fontspec) Italic},BoldFont={Fira Sans},BoldItalicFont={Fira Sans +(fontspec) Italic}]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->"FiraSansLight:mode=node;script=latn;language=dflt;+t +lig;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->"FiraSansLight:mode=node;script=latn;language=dflt;+t +lig;+smcp;" +(fontspec) - 'bold' (b/n) with NFSS spec.: +(fontspec) <->"FiraSans:mode=node;script=latn;language=dflt;+tlig;" + +(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: +(fontspec) <->"FiraSans:mode=node;script=latn;language=dflt;+tlig;+ +smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->"FiraSansLightItalic:mode=node;script=latn;language=d +flt;+tlig;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->"FiraSansLightItalic:mode=node;script=latn;language=d +flt;+tlig;+smcp;" +(fontspec) - 'bold italic' (b/it) with NFSS spec.: +(fontspec) <->"FiraSansItalic:mode=node;script=latn;language=dflt;+ +tlig;" +(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: +(fontspec) <->"FiraSansItalic:mode=node;script=latn;language=dflt;+ +tlig;+smcp;" + + +Package fontspec Info: Could not resolve font "FiraMonoMedium/I" (it probably +(fontspec) doesn't exist). + +luaotfload | aux : font no 37 (nil) does not define feature smcp for script latn + with language dflt +luaotfload | aux : font no 38 (nil) does not define feature smcp for script latn + with language dflt +luaotfload | aux : font no 39 (nil) does not define feature smcp for script latn + with language dflt +luaotfload | aux : font no 40 (nil) does not define feature smcp for script latn + with language dflt + +Package fontspec Info: Font family 'FiraMono(0)' created for font 'Fira Mono' +(fontspec) with options +(fontspec) [WordSpace={1,0,0},HyphenChar=None,PunctuationSpace=Word +Space,BoldFont={Fira +(fontspec) Mono Medium}]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->"FiraMono:mode=node;script=latn;language=dflt;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) and font adjustment code: +(fontspec) \fontdimen 2\font =1\fontdimen 2\font \fontdimen 3\font +(fontspec) =0\fontdimen 3\font \fontdimen 4\font =0\fontdimen +(fontspec) 4\font \fontdimen 7\font =0\fontdimen 2\font +(fontspec) \tex_hyphenchar:D \font =-1\scan_stop: +(fontspec) - 'bold' (b/n) with NFSS spec.: +(fontspec) <->"FiraMonoMedium:mode=node;script=latn;language=dflt;" + +(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: +(fontspec) and font adjustment code: +(fontspec) \fontdimen 2\font =1\fontdimen 2\font \fontdimen 3\font +(fontspec) =0\fontdimen 3\font \fontdimen 4\font =0\fontdimen +(fontspec) 4\font \fontdimen 7\font =0\fontdimen 2\font +(fontspec) \tex_hyphenchar:D \font =-1\scan_stop: +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->"FiraMono/I:mode=node;script=latn;language=dflt;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) and font adjustment code: +(fontspec) \fontdimen 2\font =1\fontdimen 2\font \fontdimen 3\font +(fontspec) =0\fontdimen 3\font \fontdimen 4\font =0\fontdimen +(fontspec) 4\font \fontdimen 7\font =0\fontdimen 2\font +(fontspec) \tex_hyphenchar:D \font =-1\scan_stop: +(fontspec) - 'bold italic' (b/it) with NFSS spec.: +(fontspec) <->"FiraMono/BI:mode=node;script=latn;language=dflt;" +(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: +(fontspec) and font adjustment code: +(fontspec) \fontdimen 2\font =1\fontdimen 2\font \fontdimen 3\font +(fontspec) =0\fontdimen 3\font \fontdimen 4\font =0\fontdimen +(fontspec) 4\font \fontdimen 7\font =0\fontdimen 2\font +(fontspec) \tex_hyphenchar:D \font =-1\scan_stop: + +)) +(/usr/local/texlive/2023/texmf-dist/tex/latex/fira/FiraSans.sty +Package: FiraSans 2022/09/17 (Bob Tennent and autoinst) Style file for Fira San +s fonts. + +(/usr/local/texlive/2023/texmf-dist/tex/latex/xkeyval/xkeyval.sty +Package: xkeyval 2022/06/16 v2.9 package option processing (HA) + +(/usr/local/texlive/2023/texmf-dist/tex/generic/xkeyval/xkeyval.tex +(/usr/local/texlive/2023/texmf-dist/tex/generic/xkeyval/xkvutils.tex +\XKV@toks=\toks52 +\XKV@tempa@toks=\toks53 +) +\XKV@depth=\count369 +File: xkeyval.tex 2014/12/03 v2.7a key=value parser (HA) +)) +(/usr/local/texlive/2023/texmf-dist/tex/latex/base/textcomp.sty +Package: textcomp 2020/02/02 v2.0n Standard LaTeX package +) + +Package fontspec Info: Font family 'FiraSans(0)' created for font 'FiraSans' +(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = +(fontspec) .otf,Ligatures=TeX,Numbers = +(fontspec) {Proportional,OldStyle},UprightFont = +(fontspec) *-Regular,ItalicFont = *-Italic,BoldFont = +(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn +;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn +;language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold' (b/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; +language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; +language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold italic' (b/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;+smcp;" + +LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be +(Font) scaled to size 8.50006pt on input line 138. + +Package fontspec Info: Font family 'FiraSans(1)' created for font 'FiraSans' +(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = +(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = +(fontspec) *-Regular,ItalicFont = *-Italic,BoldFont = +(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn +;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn +;language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold' (b/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; +language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; +language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold italic' (b/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;+smcp;" + + +Package fontspec Info: Font family 'FiraSans(2)' created for font 'FiraSans' +(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = +(fontspec) .otf,Numbers = {Proportional,Lining},UprightFont = +(fontspec) *-Regular,ItalicFont = *-Italic,BoldFont = +(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn +;language=dflt;+tlig;+pnum;+lnum;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn +;language=dflt;+tlig;+pnum;+lnum;+smcp;" +(fontspec) - 'bold' (b/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+lnum;" +(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+lnum;+smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; +language=dflt;+tlig;+pnum;+lnum;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; +language=dflt;+tlig;+pnum;+lnum;+smcp;" +(fontspec) - 'bold italic' (b/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+lnum;" +(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+lnum;+smcp;" + + +Package fontspec Info: Font family 'FiraSans(3)' created for font 'FiraSans' +(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = +(fontspec) .otf,Numbers = {Monospaced,OldStyle},UprightFont = +(fontspec) *-Regular,ItalicFont = *-Italic,BoldFont = +(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn +;language=dflt;+tlig;+tnum;+onum;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn +;language=dflt;+tlig;+tnum;+onum;+smcp;" +(fontspec) - 'bold' (b/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+tnum;+onum;" +(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+tnum;+onum;+smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; +language=dflt;+tlig;+tnum;+onum;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; +language=dflt;+tlig;+tnum;+onum;+smcp;" +(fontspec) - 'bold italic' (b/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+tnum;+onum;" +(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+tnum;+onum;+smcp;" + + +Package fontspec Info: Font family 'FiraSans(4)' created for font 'FiraSans' +(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = +(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = +(fontspec) *-Thin,ItalicFont = *-ThinItalic,BoldFont = +(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Thin.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Thin.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold' (b/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-ThinItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-ThinItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold italic' (b/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;+smcp;" + + +Package fontspec Info: Font family 'FiraSans(5)' created for font 'FiraSans' +(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = +(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = +(fontspec) *-Light,ItalicFont = *-LightItalic,BoldFont = +(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Light.otf]:mode=node;script=latn;l +anguage=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Light.otf]:mode=node;script=latn;l +anguage=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold' (b/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-LightItalic.otf]:mode=node;script= +latn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-LightItalic.otf]:mode=node;script= +latn;language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold italic' (b/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;+smcp;" + + +Package fontspec Info: Font family 'FiraSans(6)' created for font 'FiraSans' +(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = +(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = +(fontspec) *-ExtraLight,ItalicFont = *-ExtraLightItalic,BoldFont = +(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-ExtraLight.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-ExtraLight.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold' (b/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-ExtraLightItalic.otf]:mode=node;sc +ript=latn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-ExtraLightItalic.otf]:mode=node;sc +ript=latn;language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold italic' (b/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;+smcp;" + + +Package fontspec Info: Font family 'FiraSans(7)' created for font 'FiraSans' +(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = +(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = +(fontspec) *-UltraLight,ItalicFont = *-UltraLightItalic,BoldFont = +(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-UltraLight.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-UltraLight.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold' (b/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-UltraLightItalic.otf]:mode=node;sc +ript=latn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-UltraLightItalic.otf]:mode=node;sc +ript=latn;language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold italic' (b/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;+smcp;" + + +Package fontspec Info: Font family 'FiraSans(8)' created for font 'FiraSans' +(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = +(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = +(fontspec) *-Medium,ItalicFont = *-MediumItalic,BoldFont = +(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Medium.otf]:mode=node;script=latn; +language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Medium.otf]:mode=node;script=latn; +language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold' (b/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-MediumItalic.otf]:mode=node;script +=latn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-MediumItalic.otf]:mode=node;script +=latn;language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold italic' (b/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;+smcp;" + + +Package fontspec Info: Font family 'FiraSans(9)' created for font 'FiraSans' +(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = +(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = +(fontspec) *-Book,ItalicFont = *-BookItalic,BoldFont = +(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Book.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Book.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold' (b/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BookItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BookItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'bold italic' (b/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+pnum;+onum;+smcp;" + + +Package fontspec Info: Font family 'FiraSans(10)' created for font 'FiraSans' +(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = +(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = +(fontspec) *-SemiBold,ItalicFont = *-SemiBoldItalic]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-SemiBold.otf]:mode=node;script=lat +n;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-SemiBold.otf]:mode=node;script=lat +n;language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-SemiBoldItalic.otf]:mode=node;scri +pt=latn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-SemiBoldItalic.otf]:mode=node;scri +pt=latn;language=dflt;+tlig;+pnum;+onum;+smcp;" + + +Package fontspec Info: Font family 'FiraSans(11)' created for font 'FiraSans' +(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = +(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = +(fontspec) *-ExtraBold,ItalicFont = *-ExtraBoldItalic]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-ExtraBold.otf]:mode=node;script=la +tn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-ExtraBold.otf]:mode=node;script=la +tn;language=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-ExtraBoldItalic.otf]:mode=node;scr +ipt=latn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-ExtraBoldItalic.otf]:mode=node;scr +ipt=latn;language=dflt;+tlig;+pnum;+onum;+smcp;" + + +Package fontspec Info: Font family 'FiraSans(12)' created for font 'FiraSans' +(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = +(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = +(fontspec) *-Heavy,ItalicFont = *-HeavyItalic]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Heavy.otf]:mode=node;script=latn;l +anguage=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Heavy.otf]:mode=node;script=latn;l +anguage=dflt;+tlig;+pnum;+onum;+smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-HeavyItalic.otf]:mode=node;script= +latn;language=dflt;+tlig;+pnum;+onum;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-HeavyItalic.otf]:mode=node;script= +latn;language=dflt;+tlig;+pnum;+onum;+smcp;" + +) (./iors.aux) +\openout1 = iors.aux + +LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 37. +LaTeX Font Info: ... okay on input line 37. +LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 37. +LaTeX Font Info: ... okay on input line 37. +LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 37. +LaTeX Font Info: ... okay on input line 37. +LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 37. +LaTeX Font Info: ... okay on input line 37. +LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 37. +LaTeX Font Info: Trying to load font information for TS1+cmr on input line 3 +7. + +(/usr/local/texlive/2023/texmf-dist/tex/latex/base/ts1cmr.fd +File: ts1cmr.fd 2022/07/10 v2.5l Standard LaTeX font definitions +) +LaTeX Font Info: ... okay on input line 37. +LaTeX Font Info: Checking defaults for TU/lmr/m/n on input line 37. +LaTeX Font Info: ... okay on input line 37. +LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 37. +LaTeX Font Info: ... okay on input line 37. +LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 37. +LaTeX Font Info: ... okay on input line 37. +LaTeX Font Info: Checking defaults for PD1/pdf/m/n on input line 37. +LaTeX Font Info: ... okay on input line 37. +LaTeX Font Info: Checking defaults for PU/pdf/m/n on input line 37. +LaTeX Font Info: ... okay on input line 37. + +*geometry* driver: auto-detecting +*geometry* detected driver: luatex +*geometry* verbose mode - [ preamble ] result: +* driver: luatex +* paper: custom +* layout: +* layoutoffset:(h,v)=(0.0pt,0.0pt) +* modes: includehead includefoot +* h-part:(L,W,R)=(28.45274pt, 398.3386pt, 28.45274pt) +* v-part:(T,H,B)=(0.0pt, 256.0748pt, 0.0pt) +* \paperwidth=455.24408pt +* \paperheight=256.0748pt +* \textwidth=398.3386pt +* \textheight=227.62207pt +* \oddsidemargin=-43.81725pt +* \evensidemargin=-43.81725pt +* \topmargin=-72.26999pt +* \headheight=14.22636pt +* \headsep=0.0pt +* \topskip=10.0pt +* \footskip=14.22636pt +* \marginparwidth=3.0pt +* \marginparsep=11.0pt +* \columnsep=10.0pt +* \skip\footins=9.0pt plus 4.0pt minus 2.0pt +* \hoffset=0.0pt +* \voffset=0.0pt +* \mag=1000 +* \@twocolumnfalse +* \@twosidefalse +* \@mparswitchfalse +* \@reversemarginfalse +* (1in=72.27pt=25.4mm, 1cm=28.453pt) + +(/usr/local/texlive/2023/texmf-dist/tex/context/base/mkii/supp-pdf.mkii +[Loading MPS to PDF converter (version 2006.09.02).] +\scratchcounter=\count370 +\scratchdimen=\dimen330 +\scratchbox=\box86 +\nofMPsegments=\count371 +\nofMParguments=\count372 +\everyMPshowfont=\toks54 +\MPscratchCnt=\count373 +\MPscratchDim=\dimen331 +\MPnumerator=\count374 +\makeMPintoPDFobject=\count375 +\everyMPtoPDFconversion=\toks55 +) (/usr/local/texlive/2023/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty +Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf +Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4 +85. + +(/usr/local/texlive/2023/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg +File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv +e +)) +Package hyperref Info: Link coloring OFF on input line 37. + +(./iors.out) (./iors.out) +\@outlinefile=\write5 + +\openout5 = iors.out +LaTeX Font Info: Overwriting symbol font `operators' in version `normal' +(Font) OT1/cmr/m/n --> OT1/cmss/m/n on input line 37. +LaTeX Font Info: Overwriting symbol font `operators' in version `bold' +(Font) OT1/cmr/bx/n --> OT1/cmss/b/n on input line 37. +\symnumbers=\mathgroup6 +\sympureletters=\mathgroup7 +LaTeX Font Info: Overwriting math alphabet `\mathrm' in version `normal' +(Font) OT1/cmss/m/n --> TU/lmr/m/n on input line 37. +LaTeX Font Info: Redeclaring math alphabet \mathbf on input line 37. +LaTeX Font Info: Overwriting math alphabet `\mathbf' in version `normal' +(Font) OT1/cmr/bx/n --> TU/FiraSans(0)/b/n on input line 37. +LaTeX Font Info: Overwriting math alphabet `\mathbf' in version `bold' +(Font) OT1/cmr/bx/n --> TU/FiraSans(0)/b/n on input line 37. +LaTeX Font Info: Redeclaring math alphabet \mathsf on input line 37. +LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `normal' +(Font) OT1/cmss/m/n --> TU/FiraSans(0)/m/n on input line 37. +LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `bold' +(Font) OT1/cmss/bx/n --> TU/FiraSans(0)/m/n on input line 37. +LaTeX Font Info: Redeclaring math alphabet \mathit on input line 37. +LaTeX Font Info: Overwriting math alphabet `\mathit' in version `normal' +(Font) OT1/cmr/m/it --> TU/FiraSans(0)/m/it on input line 37. +LaTeX Font Info: Overwriting math alphabet `\mathit' in version `bold' +(Font) OT1/cmr/bx/it --> TU/FiraSans(0)/m/it on input line 37. + +LaTeX Font Info: Redeclaring math alphabet \mathtt on input line 37. +LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `normal' +(Font) OT1/cmtt/m/n --> TU/FiraMono(0)/m/n on input line 37. +LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `bold' +(Font) OT1/cmtt/m/n --> TU/FiraMono(0)/m/n on input line 37. +LaTeX Font Info: Overwriting symbol font `numbers' in version `bold' +(Font) TU/FiraSans(0)/m/n --> TU/FiraSans(0)/b/n on input line + 37. +LaTeX Font Info: Overwriting symbol font `pureletters' in version `bold' +(Font) TU/FiraSans(0)/m/it --> TU/FiraSans(0)/b/it on input li +ne 37. +LaTeX Font Info: Overwriting math alphabet `\mathrm' in version `bold' +(Font) OT1/cmss/b/n --> TU/lmr/b/n on input line 37. +LaTeX Font Info: Overwriting math alphabet `\mathbf' in version `bold' +(Font) TU/FiraSans(0)/b/n --> TU/FiraSans(0)/b/n on input line + 37. +LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `bold' +(Font) TU/FiraSans(0)/m/n --> TU/FiraSans(0)/b/n on input line + 37. +LaTeX Font Info: Overwriting math alphabet `\mathit' in version `bold' +(Font) TU/FiraSans(0)/m/it --> TU/FiraSans(0)/b/it on input li +ne 37. +LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `bold' +(Font) TU/FiraMono(0)/m/n --> TU/FiraMono(0)/b/n on input line + 37. + +(/usr/local/texlive/2023/texmf-dist/tex/latex/translator/translator-basic-dicti +onary-English.dict +Dictionary: translator-basic-dictionary, Language: English +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/translator/translator-bibliograph +y-dictionary-English.dict +Dictionary: translator-bibliography-dictionary, Language: English +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/translator/translator-environment +-dictionary-English.dict +Dictionary: translator-environment-dictionary, Language: English +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/translator/translator-months-dict +ionary-English.dict +Dictionary: translator-months-dictionary, Language: English +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/translator/translator-numbers-dic +tionary-English.dict +Dictionary: translator-numbers-dictionary, Language: English +) +(/usr/local/texlive/2023/texmf-dist/tex/latex/translator/translator-theorem-dic +tionary-English.dict +Dictionary: translator-theorem-dictionary, Language: English +) +\c@lstlisting=\count376 + (./iors.nav) +LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be +(Font) scaled to size 4.25003pt on input line 37. +LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be +(Font) scaled to size 5.95004pt on input line 37. +LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be +(Font) scaled to size 12.24008pt on input line 39. +LaTeX Font Info: Font shape `TU/FiraSans(0)/b/n' will be +(Font) scaled to size 12.24008pt on input line 39. +LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be +(Font) scaled to size 10.20007pt on input line 39. +LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be +(Font) scaled to size 7.65005pt on input line 39. + +Overfull \vbox (13.79993pt too high) detected at line 39 + [] + +[1 + +{/usr/local/texlive/2023/texmf-var/fonts/map/pdftex/updmap/pdftex.map}] +LaTeX Font Info: Trying to load font information for U+msa on input line 53. + + +(/usr/local/texlive/2023/texmf-dist/tex/latex/amsfonts/umsa.fd +File: umsa.fd 2013/01/14 v3.01 AMS symbols A +) +LaTeX Font Info: Trying to load font information for U+msb on input line 53. + + +(/usr/local/texlive/2023/texmf-dist/tex/latex/amsfonts/umsb.fd +File: umsb.fd 2013/01/14 v3.01 AMS symbols B +) +LaTeX Font Info: Font shape `TU/FiraSans(0)/m/it' will be +(Font) scaled to size 8.50006pt on input line 53. +LaTeX Font Info: Font shape `TU/FiraSans(0)/m/it' will be +(Font) scaled to size 5.95004pt on input line 53. +LaTeX Font Info: Font shape `TU/FiraSans(0)/m/it' will be +(Font) scaled to size 4.25003pt on input line 53. +LaTeX Font Info: Font shape `TU/FiraSans(0)/b/n' will be +(Font) scaled to size 10.20007pt on input line 53. +LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be +(Font) scaled to size 3.40002pt on input line 53. + [2 + +] +LaTeX Font Info: Font shape `TU/FiraSans(0)/b/n' will be +(Font) scaled to size 8.50006pt on input line 66. + [3 + +] + +Package fontspec Info: Font family 'FiraSans(13)' created for font 'FiraSans' +(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = +(fontspec) .otf,Ligatures=TeX,Numbers = +(fontspec) {Proportional,OldStyle},UprightFont = +(fontspec) *-Regular,ItalicFont = *-Italic,BoldFont = +(fontspec) *-Bold,BoldItalicFont = +(fontspec) *-BoldItalic,Numbers={Monospaced}]. +(fontspec) +(fontspec) This font family consists of the following NFSS +(fontspec) series/shapes: +(fontspec) +(fontspec) - 'normal' (m/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn +;language=dflt;+tlig;+onum;+tnum;" +(fontspec) - 'small caps' (m/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn +;language=dflt;+tlig;+onum;+tnum;+smcp;" +(fontspec) - 'bold' (b/n) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+onum;+tnum;" +(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la +nguage=dflt;+tlig;+onum;+tnum;+smcp;" +(fontspec) - 'italic' (m/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; +language=dflt;+tlig;+onum;+tnum;" +(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; +language=dflt;+tlig;+onum;+tnum;+smcp;" +(fontspec) - 'bold italic' (b/it) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+onum;+tnum;" +(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: +(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l +atn;language=dflt;+tlig;+onum;+tnum;+smcp;" + +LaTeX Font Info: Font shape `TU/FiraSans(13)/m/n' will be +(Font) scaled to size 7.65005pt on input line 91. +LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be +(Font) scaled to size 5.10004pt on input line 91. +LaTeX Font Info: Font shape `TU/FiraSans(0)/m/it' will be +(Font) scaled to size 7.65005pt on input line 91. +LaTeX Font Info: Font shape `TU/FiraSans(0)/m/it' will be +(Font) scaled to size 5.10004pt on input line 91. +LaTeX Font Info: Font shape `TU/FiraSans(13)/b/n' will be +(Font) scaled to size 7.65005pt on input line 91. + +Overfull \vbox (1.61246pt too high) detected at line 91 + [] + +[4 + +] +\openout4 = iors.vrb + (./iors.vrb) [5 + +] +LaTeX Font Info: Font shape `TU/FiraSans(13)/m/n' will be +(Font) scaled to size 5.95004pt on input line 139. +LaTeX Font Info: Font shape `TU/FiraSans(13)/b/n' will be +(Font) scaled to size 5.95004pt on input line 139. +LaTeX Font Info: Font shape `TU/FiraSans(0)/b/n' will be +(Font) scaled to size 5.95004pt on input line 139. + [6 + +] [7 + +] +\openout4 = iors.vrb + (./iors.vrb) [8 + +] +\openout4 = iors.vrb + (./iors.vrb) [9 + +] +\openout4 = iors.vrb + (./iors.vrb) +[10 + +] [11 + +] +LaTeX Font Info: Font shape `TU/FiraSans(0)/b/n' will be +(Font) scaled to size 7.65005pt on input line 263. + [12 + +] [13 + +] +\openout4 = iors.vrb + (./iors.vrb) [14 + +] [15 + +] [16 + +] +\tf@nav=\write6 + +\openout6 = iors.nav +\tf@toc=\write7 + +\openout7 = iors.toc +\tf@snm=\write8 + +\openout8 = iors.snm + (./iors.aux) +Package rerunfilecheck Info: File `iors.out' has not changed. +(rerunfilecheck) Checksum: D41D8CD98F00B204E9800998ECF8427E;0. +) + +Here is how much of LuaTeX's memory you used: + 31159 strings out of 478285 + 244236,1977958 words of node,token memory allocated + 10339 words of node memory still in use: + 35 hlist, 8 vlist, 5 rule, 75 disc, 6 local_par, 2 math, 185 glue, 185 kern, +19 penalty, 850 glyph, 77 attribute, 71 glue_spec, 77 attribute_list, 4 write, 1 +6 pdf_colorstack nodes + avail lists: 1:3,2:3847,3:247,4:266,5:6816,6:106,7:6156,8:42,9:13512,10:41,11 +:753 + 49861 multiletter control sequences out of 65536+600000 + 147 fonts using 43462351 bytes + 128i,13n,131p,1870b,652s stack positions out of 10000i,1000n,20000p,200000b,200000s + + +Output written on iors.pdf (16 pages, 128936 bytes). + +PDF statistics: 185 PDF objects out of 1000 (max. 8388607) + 131 compressed objects within 2 object streams + 33 named destinations out of 1000 (max. 131072) + 16 words of extra memory for PDF output out of 10000 (max. 100000000) + diff --git a/docs/paper-mods/slides/iors.nav b/docs/paper-mods/slides/iors.nav new file mode 100644 index 0000000..d5ca61a --- /dev/null +++ b/docs/paper-mods/slides/iors.nav @@ -0,0 +1,37 @@ +\headcommand {\slideentry {0}{0}{1}{1/1}{}{0}} +\headcommand {\beamer@framepages {1}{1}} +\headcommand {\slideentry {0}{0}{2}{2/2}{}{0}} +\headcommand {\beamer@framepages {2}{2}} +\headcommand {\slideentry {0}{0}{3}{3/3}{}{0}} +\headcommand {\beamer@framepages {3}{3}} +\headcommand {\slideentry {0}{0}{4}{4/4}{}{0}} +\headcommand {\beamer@framepages {4}{4}} +\headcommand {\slideentry {0}{0}{5}{5/5}{}{0}} +\headcommand {\beamer@framepages {5}{5}} +\headcommand {\slideentry {0}{0}{6}{6/6}{}{0}} +\headcommand {\beamer@framepages {6}{6}} +\headcommand {\slideentry {0}{0}{7}{7/7}{}{0}} +\headcommand {\beamer@framepages {7}{7}} +\headcommand {\slideentry {0}{0}{8}{8/8}{}{0}} +\headcommand {\beamer@framepages {8}{8}} +\headcommand {\slideentry {0}{0}{9}{9/9}{}{0}} +\headcommand {\beamer@framepages {9}{9}} +\headcommand {\slideentry {0}{0}{10}{10/10}{}{0}} +\headcommand {\beamer@framepages {10}{10}} +\headcommand {\slideentry {0}{0}{11}{11/11}{}{0}} +\headcommand {\beamer@framepages {11}{11}} +\headcommand {\slideentry {0}{0}{12}{12/12}{}{0}} +\headcommand {\beamer@framepages {12}{12}} +\headcommand {\slideentry {0}{0}{13}{13/13}{}{0}} +\headcommand {\beamer@framepages {13}{13}} +\headcommand {\slideentry {0}{0}{14}{14/14}{}{0}} +\headcommand {\beamer@framepages {14}{14}} +\headcommand {\slideentry {0}{0}{15}{15/15}{}{0}} +\headcommand {\beamer@framepages {15}{15}} +\headcommand {\slideentry {0}{0}{16}{16/16}{}{0}} +\headcommand {\beamer@framepages {16}{16}} +\headcommand {\beamer@partpages {1}{16}} +\headcommand {\beamer@subsectionpages {1}{16}} +\headcommand {\beamer@sectionpages {1}{16}} +\headcommand {\beamer@documentpages {16}} +\headcommand {\gdef \inserttotalframenumber {15}} diff --git a/docs/paper-mods/slides/iors.out b/docs/paper-mods/slides/iors.out new file mode 100644 index 0000000..e69de29 diff --git a/docs/paper-mods/slides/iors.pdf b/docs/paper-mods/slides/iors.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5712224230e99da6f3d5a6e57ee4fa0a45e4f74c GIT binary patch literal 128936 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 literal 0 HcmV?d00001 diff --git a/docs/paper-mods/slides/iors.snm b/docs/paper-mods/slides/iors.snm new file mode 100644 index 0000000..e69de29 diff --git a/docs/paper-mods/slides/iors.tex b/docs/paper-mods/slides/iors.tex new file mode 100644 index 0000000..47b3ba5 --- /dev/null +++ b/docs/paper-mods/slides/iors.tex @@ -0,0 +1,343 @@ +\documentclass[10pt,aspectratio=169]{beamer} + +\usepackage{listings} +\usepackage{xcolor} +\usepackage{booktabs} +\usepackage{array} + +\usetheme{metropolis} +\usepackage[scale=0.85]{FiraSans} + +\definecolor{warpaccent}{HTML}{2C3E50} +\definecolor{warpcode}{HTML}{1B5E20} +\definecolor{warpgray}{HTML}{555555} + +\setbeamercolor{frametitle}{bg=warpaccent} +\setbeamercolor{title separator}{fg=warpaccent} +\setbeamertemplate{frame numbering}[fraction] + +\lstset{ + basicstyle=\ttfamily\footnotesize, + keywordstyle=\color{warpaccent}\bfseries, + commentstyle=\color{warpgray}\itshape, + stringstyle=\color{warpcode}, + showstringspaces=false, + morekeywords={Self, dyn, impl, async, await, fn, type, trait, where, Result, Vec, Box, Option, mut, pub, struct, let, return, use}, + keepspaces=true, + columns=fullflexible, + frame=none, + breaklines=true, +} + +\title{Warp Modularity} +\subtitle{From paper IORs to a code-level interface} +\author{Internal note} +\date{} + +\begin{document} + +\maketitle + +% -------------------------------------------------------------------- +\begin{frame}{What we want} +A single interface every Warp phase satisfies, that is visibly the same +shape as the paper's IOR signature. + +\vspace{0.5em} +\begin{itemize} + \item \texttt{lib.rs::prove} reads as a chain of typed transitions. + \item Each transition: $(\text{stmt}, \text{wit}, \text{or}_\text{in}) \mapsto (\text{stmt}', \text{or}_\text{out})$. + \item Adding a phase: a struct + a trait impl. No bespoke plumbing. +\end{itemize} + +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{Why Warp wasn't easy modularly} +The paper IOR formalism doesn't share state across IORs. Real Warp does. + +\vspace{0.4em} +\textbf{Three concrete frictions:} +\begin{enumerate} + \item \textbf{Shared oracle $f$.} TwinConstraint, OOD, Batching, Proximity all consume the same codeword. A literal IOR-per-phase would rematerialise. + \item \textbf{Transcript ordering coupling.} Shift-query indices are squeezed once and consumed by both Batching and Proximity. No phase ``owns'' the squeeze. + \item \textbf{Statement / witness / oracle conflation.} The pre-refactor function had $\sim$10 mixed-purpose args; the paper's tripartite split was invisible. +\end{enumerate} +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{What an IOR signature is} +The Warp paper structures the protocol as a sequence of Interactive Oracle Reductions, each one having the composition rule: +\[ A: (\mathrm{stmt}_A, \mathrm{wit}_A) \mapsto (\mathrm{stmt}_A', \mathsf{O}[f]) \] +\[ B: (\mathrm{stmt}_B, \mathsf{O}[f]) \mapsto \mathrm{stmt}_B' \] +\[ A;B: (\mathrm{stmt}_A, \mathrm{stmt}_B, \mathrm{wit}_A) \mapsto (\mathrm{stmt}_A', \mathrm{stmt}_B') \] + +\vspace{0.4em} +Five typed components per IOR: + +\begin{center}\small +\begin{tabular}{lcc} +\toprule +& \textbf{Prover} & \textbf{Verifier} \\ +\midrule +\texttt{Statement} & \checkmark & \checkmark \\ +\texttt{Witness} & \checkmark & --- \\ +\texttt{InputOracles} & by data & by handle \\ +\texttt{ReducedStatement} & \checkmark & \checkmark \\ +\texttt{OutputOracles} & by data & by handle \\ +\bottomrule +\end{tabular} +\end{center} +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}[fragile]{The IOR trait we built} +v1 was a thin \texttt{ProverPhase} with opaque \texttt{Output}; v2 mirrors the paper: +\begin{lstlisting} +pub trait IOR { + type Statement; type Witness; + type ProverInputs; type VerifierInputs; + type ReducedStatement; + type ProverOutputs; type VerifierOutputs; + + fn prove(&self, ts: &mut ProverState, stmt: &Self::Statement, + wit: Self::Witness, inputs: Self::ProverInputs) + -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError>; + + fn verify<'a>(&self, ts: &mut VerifierState<'a>, + stmt: &Self::Statement, inputs: Self::VerifierInputs) + -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError>; +} +\end{lstlisting} +33/33 tests green. \texttt{Statement}/\texttt{ReducedStatement} shared; oracle types role-split. +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{Per-phase mapping} +\scriptsize +\begin{center} +\begin{tabular}{lp{1.4cm}p{1.2cm}p{1.4cm}p{1.6cm}p{1.5cm}} +\toprule +& \textbf{Stmt} & \textbf{Wit} & \textbf{Pr.\,In} & \textbf{Reduced} & \textbf{Pr.\,Out} \\ +\midrule +\texttt{Pesat} & $(l_1, \log m)$ & wits & --- & $(\mu, \tau)$ & cw + tree \\ +\texttt{TwinC.} & acc + $\mu$/$\tau$ & acc-w + ins & cw + acc-cw & $\gamma, \zeta_0, \beta_\tau,$ defer & $\mathsf{O}[f] + z$ \\ +\texttt{Ood} & $(s, \log n)$ & --- & $\mathsf{O}[f]$ & samples + ans & --- \\ +\texttt{Batching} & $\zeta$\,+\,$s,t,\log n$ & --- & $\mathsf{O}[f]$ & $\alpha$ & $\mu$ \\ +\texttt{Proximity} & queries\,+\,$l_2,t$ & --- & trees + cw & $()$ & paths + ans \\ +\bottomrule +\end{tabular} +\end{center} + +\vspace{0.4em} +\textbf{Asymmetries are now structural:} +\begin{itemize}\scriptsize + \item \texttt{Pesat} \,---\, empty \texttt{ProverInputs} (it's the source). + \item \texttt{Proximity} \,---\, empty \texttt{Reduced} (it's a check). + \item \texttt{Ood} / \texttt{Batching} \,---\, empty \texttt{Witness}. +\end{itemize} +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{Single-protocol view: a candid review} +\textbf{What's good.} +\begin{itemize}\small + \item Paper-aligned types \,---\, the \texttt{IOR} trait and the paper's IOR composition rule line up directly. + \item Real verifier symmetry \,---\, \texttt{TwinConstraint::verify} / \texttt{Batching::verify} are first-class trait impls. + \item Honest oracle role split \,---\, prover sees data, verifier sees commitments. + \item Empty-type asymmetries are visible (\texttt{Reduced = ()} for checks). + \item 33/33 tests green throughout. +\end{itemize} + +\vspace{0.4em} +\textbf{What's not.} +\begin{itemize}\small + \item Seven associated types per phase \,---\, real cognitive load. + \item Lifetimes \& \texttt{PhantomData} proliferate; structs carry \texttt{<'a, F, \dots>} for anchoring. + \item No useful composition combinators \,---\, see next slide. + \item No type-level ordering enforcement; transcript order is implicit. + \item Orchestrator got longer, not shorter \,---\, $\sim$10 lines per phase invocation. +\end{itemize} +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}[fragile]{Composition \,---\, what the trait can't do} +\begin{lstlisting} +pub struct Sequence(A, B); +impl IOR for Sequence +where + B::Statement: From, + B::ProverInputs: From, + // ... and the witness has to come from somewhere ... +{ /* run A, derive B's inputs, run B */ } +\end{lstlisting} + +\textbf{Why no useful generics fall out:} +\begin{itemize}\small + \item \texttt{From} impls are protocol-specific \,---\, written per \texttt{(A, B)} pair, no leverage. + \item ``Between-phases'' glue is itself protocol logic (WARP between TC and OOD: compute $\eta, \nu_0$, build new Merkle tree, write transcript). Doesn't fit either phase's signature. + \item Witnesses don't thread cleanly across the boundary. +\end{itemize} + +\textbf{What unlocks it:} a canonical \texttt{Claim} algebra of shared shapes \,---\, next two slides. +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}[fragile]{Claim \,---\, the shared vocabulary} +Lift ``what's being claimed'' from per-protocol bags into a small shared enum. + +\begin{lstlisting} +pub enum Claim { + Sumcheck(SumcheckClaim), // p sums to v on {0,1}^n + Eval(EvalClaim), // f_hat(zeta) = y + Proximity(ProximityClaim), // codeword delta-close to code + ZeroCheck(ZeroCheckClaim), // p == 0 on {0,1}^n + Batched(Box>), +} +\end{lstlisting} + +Operations on these claims become \emph{protocol-agnostic}: +\begin{lstlisting} +pub fn reduce_sumcheck( + claim: SumcheckClaim, + prover: &mut impl SumcheckProver, + transcript: &mut T, +) -> EvalClaim { ... } +\end{lstlisting} + +STIR uses it. WHIR uses it. WARP uses it. The \emph{transformation} is the abstraction \,---\, not the protocol. +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}[fragile]{Protocol becomes a script over claim transformations} +\begin{lstlisting} +fn warp_round(...) -> Claim { + let c = Claim::ZeroCheck(...); // R1CS satisfaction + let c = c.bundle_with(...); // -> SumcheckClaim + let c = reduce_sumcheck(c, ...); // -> EvalClaim at gamma + let oods = ood_sample(...); // s EvalClaims + let qs = shift_query_claims(...); // t more + let c = batch_eval_claims(vec![...]); // -> 1 EvalClaim + let c = reduce_sumcheck(c, ...); // -> EvalClaim at alpha + Claim::Eval(c) +} +\end{lstlisting} + +Each line is a typed claim transformation. Protocol identity is the \emph{script}; the transformations are reusable. + +\textbf{Limits + cost.} Only the public claim flow gets typified \,---\, witnesses, transcript threading, setup, and proof shapes stay per-protocol. Canonical shapes only emerge from 3+ implementations, and soundness arguments must thread through the algebra. Multi-protocol research thrust, not a tactical project. +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{Reframing: a family of protocols} +The single-protocol view above was honest \,---\, but the actual goal isn't ``a clean WARP.'' + +\vspace{0.4em} +We design protocols like \textbf{STIR}, \textbf{WHIR}, \textbf{WARP} all the time. They share: +\begin{itemize}\small + \item Encode-and-commit (codeword \,---\, Reed-Solomon, or otherwise). + \item Sumcheck reductions (different flavours: degree-1, multilinear, fused twin-constraint, $\dots$). + \item Out-of-domain (OOD) sampling for proximity boost. + \item Shift-query opening with auth paths. + \item Batching sumchecks that fold many claims into one. + \item Accumulator updates / final consistency. +\end{itemize} + +\vspace{0.4em} +\textbf{The bar for the IOR trait then shifts:} not ``does this clean up WARP?'' but ``is this the right backbone for a family?'' +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{The toolkit, audited} +What practitioners writing STIR / WHIR / WARP \emph{already} have: +\begin{itemize}\small + \item \textbf{Transcripts.} \texttt{spongefish} 0.7 \,---\, de facto in this corner of the ecosystem. Practitioners use it directly. No abstraction needed. + \item \textbf{Sumchecks.} \texttt{effsc} \,---\, \texttt{SumcheckProver}, \texttt{RoundPolyEvaluator}, \texttt{InnerProductProver}, \texttt{CoefficientProverLSB}, \texttt{MultilinearProver}, hypercube + protogalaxy utilities. WARP uses 9 of 10 effsc primitives already. + \item \textbf{Linear codes.} \texttt{ark-codes::LinearCode} \,---\, encode, code\_len. + \item \textbf{Merkle commitments.} \texttt{ark-crypto-primitives::merkle\_tree} \,---\, generic over leaf shape, hashes. + \item \textbf{Oracle struct.} \texttt{Vec} codeword + lazy MLE cache. Same storage shape across STIR / WHIR / WARP. +\end{itemize} + +\vspace{0.4em} +The primitive layer is mostly done. The discussion is really about \emph{integration} \,---\, how primitives compose, and what shape protocol authors copy. +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{What practitioner experience actually needs} +Designing a STIR / WHIR / WARP-style protocol is hard. The friction is rarely ``I need a new sumcheck primitive.'' It's: +\begin{itemize}\small + \item \textbf{Tying primitives together correctly.} LinearCode + Merkle is two crates; integration is per-protocol boilerplate. + \item \textbf{Knowing the right phase decomposition.} What's a phase? What's its statement? Where's the witness? When does the verifier act? + \item \textbf{Threading transcript ordering without bugs.} Squeeze before write, derive challenges in the right segments. + \item \textbf{Maintaining paper-code alignment.} The paper's IOR composition is the contract; reviewers expect to see it in code. + \item \textbf{Debugging when soundness fails.} ``Where in this 600-line \texttt{verify} did the rejection happen?'' +\end{itemize} + +\vspace{0.4em} +The fellow: \emph{practitioners benefit from patterns + examples + shared primitives}, not from cross-protocol trait contracts. That's the lever. +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}[fragile]{The one new abstraction: \texttt{CodeCommitment}} +The single integration gap not already covered by the toolkit: + +\begin{lstlisting} +pub trait CodeCommitment { + type Commitment; + type Opening; + + fn commit(&self, codeword: &[F]) -> Self::Commitment; + fn open_index(&self, codeword: &[F], i: usize) -> Self::Opening; + fn verify_opening( + &self, c: &Self::Commitment, i: usize, val: F, o: &Self::Opening, + ) -> bool; +} +\end{lstlisting} + +\vspace{0.4em} +Default impl: \texttt{LinearCode} + Merkle config from \texttt{ark-crypto-primitives}. +\begin{itemize}\small + \item Wraps the only integration practitioners currently re-roll per protocol. + \item Small (one trait, one default impl), focused, validated by WARP migrating onto it. +\end{itemize} +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{What we're for, positively} +\textbf{Keep \texttt{IOR} as a structural template, not a cross-protocol contract.} +\begin{itemize}\small + \item It's the shape WARP-style protocols copy. The paper section on accumulator-as-state makes it legible. + \item Other protocols follow as a \emph{pattern}, get shared mental model + idioms, without being forced into one trait. +\end{itemize} + +\textbf{Lean on the existing primitive layer.} +\begin{itemize}\small + \item \texttt{effsc}, \texttt{spongefish}, \texttt{ark-codes}, \texttt{ark-crypto-primitives::merkle\_tree}. + \item Already strong. The discipline is to use them, not reinvent. +\end{itemize} + +\textbf{Add \texttt{CodeCommitment}.} The one missing integration trait. WARP migrates onto it; STIR-lite plugs into it. + +\textbf{Make WARP the reference implementation.} A new protocol designer reads \texttt{src/protocol/phases/}, copies the IOR-shaped pattern, plugs in their primitives. + +\textbf{Document the pattern.} Short ``how to design a Warp-style protocol'' guide \,---\, narrative, not a trait. The actual lever for practitioner ergonomics. +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{Where this leaves us \,---\, the next move} +\textbf{Paper section (accumulator as typed state).} Describe the IOR trait as a paper-aligned realisation \emph{of WARP's protocol structure}, framed as a \emph{template other protocols can follow}, not a contract they must. + +\vspace{0.4em} +\textbf{Paper section (structured sumcheck primitive).} The fused $\alpha/\beta/\tau$-fold inside TwinConstraint is the natural extraction \,---\, lives in \texttt{effsc} as a new \texttt{RoundPolyEvaluator}-shaped impl, citable from outside Warp. + +\vspace{0.4em} +\textbf{Add \texttt{CodeCommitment}} \,---\, small focused trait, ships in a session. Migrate WARP onto it. + +\vspace{0.4em} +\textbf{Implement a second protocol.} STIR-lite or WHIR-lite, against the same primitives + IOR-shaped pattern. The friction it surfaces is the most reliable guide for the next iteration. + +\vspace{0.4em} +\textbf{Write the ``how to design a Warp-style protocol'' guide.} Walk through phase decomposition, primitive choices, IOR shape. Narrative document in \texttt{docs/}. The actual practitioner-facing artifact. +\end{frame} + +\end{document} diff --git a/docs/paper-mods/slides/iors.toc b/docs/paper-mods/slides/iors.toc new file mode 100644 index 0000000..e69de29 diff --git a/docs/paper-mods/slides/iors.vrb b/docs/paper-mods/slides/iors.vrb new file mode 100644 index 0000000..f3b76af --- /dev/null +++ b/docs/paper-mods/slides/iors.vrb @@ -0,0 +1,22 @@ +\frametitle{The one new abstraction: \texttt {CodeCommitment}} +The single integration gap not already covered by the toolkit: + +\begin{lstlisting} +pub trait CodeCommitment { + type Commitment; + type Opening; + + fn commit(&self, codeword: &[F]) -> Self::Commitment; + fn open_index(&self, codeword: &[F], i: usize) -> Self::Opening; + fn verify_opening( + &self, c: &Self::Commitment, i: usize, val: F, o: &Self::Opening, + ) -> bool; +} +\end{lstlisting} + +\vspace{0.4em} +Default impl: \texttt{LinearCode} + Merkle config from \texttt{ark-crypto-primitives}. +\begin{itemize}\small + \item Wraps the only integration practitioners currently re-roll per protocol. + \item Small (one trait, one default impl), focused, validated by WARP migrating onto it. +\end{itemize} diff --git a/src/lib.rs b/src/lib.rs index ee1faa8..8b57bf8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,23 +11,14 @@ use ark_ff::{Field, PrimeField}; use ark_poly::{DenseMultilinearExtension, Polynomial}; use ark_std::log2; use config::WARPConfig; -use effsc::{ - hypercube::{compute_hypercube_eq_evals, Ascending}, - verifier::sumcheck_verify, -}; +use effsc::hypercube::{compute_hypercube_eq_evals, Ascending}; use protocol::query::QueryIndices; -use protocol::transcript::{ - absorb_instances, derive_between_sumchecks, derive_pre_twin_constraint, parse_statement, - BetweenSumchecks, EffscVerifierTranscript, PreTwinConstraint, -}; +use protocol::transcript::{absorb_instances, parse_statement}; use relations::{r1cs::R1CSConstraints, BundledPESAT}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; use utils::scale_and_sum; -use utils::{ - concat_slices, - poly::{eq_poly, eq_poly_non_binary}, -}; +use utils::{concat_slices, poly::eq_poly}; pub mod config; pub mod constraints; @@ -44,8 +35,14 @@ pub mod utils; use error::{DeciderError, ProverError, VerifierError}; use protocol::phases::{ - batching::Batching, ood::Ood, pesat::Pesat, proximity::Proximity, - proximity::ProximityVerify, twin_constraint::TwinConstraint, ProverPhase, VerifierPhase, + batching::{Batching, BatchingProverInputs, BatchingStatement, BatchingVerifierInputs}, + ood::{Ood, OodProverInputs, OodStatement}, + pesat::{Pesat, PesatStatement, PesatWitness}, + proximity::{Proximity, ProximityProverInputs, ProximityStatement, ProximityVerifierInputs}, + twin_constraint::{ + TwinConstraint, TwinConstraintProverInputs, TwinConstraintStatement, TwinConstraintWitness, + }, + IOR, }; pub trait BoolResult { @@ -159,47 +156,63 @@ impl< } = acc_witness; // Phase 2: PESAT — emit oracles (codewords), commit, squeeze τs. - let pesat = Pesat:: { + let pesat_phase = Pesat:: { code: &self.params.code, mt_leaf_hash_params: &self.params.mt_leaf_hash_params, mt_two_to_one_hash_params: &self.params.mt_two_to_one_hash_params, - witnesses: &witnesses, - l1, - log_m, _phantom: PhantomData, - } - .prove(prover_state)?; + }; + let (pesat_red, pesat_out) = pesat_phase.prove( + prover_state, + &PesatStatement { l1, log_m }, + PesatWitness { + witnesses: &witnesses, + }, + (), + )?; // Phase 3a: twin-constraint sumcheck. - let tc = TwinConstraint:: { - fresh_codewords: &pesat.codewords, - fresh_taus: pesat.taus, - acc_instance, - acc_witness_f: &acc_fs, - acc_witness_w: &acc_ws, - instances: &instances, - witnesses: &witnesses, + let tc_phase = TwinConstraint:: { r1cs: self.params.p.constraints(), - log_l, - log_m, - log_n, - } - .prove(prover_state)?; + _phantom: PhantomData, + }; + let (tc_red, tc_out) = tc_phase.prove( + prover_state, + &TwinConstraintStatement { + acc_instance, + l1_mus: pesat_red.mus.clone(), + l1_taus: pesat_red.taus, + log_l, + log_m, + log_n, + }, + TwinConstraintWitness { + acc_witness_w: &acc_ws, + instances: &instances, + witnesses: &witnesses, + }, + TwinConstraintProverInputs { + fresh_codewords: &pesat_out.codewords, + acc_codewords: &acc_fs, + }, + )?; // Phase 3b: bundled η, ν₀, new commitment, absorb — the "emit new // oracle + claims" step between twin-constraint and OOD. - let beta_eq_evals = (0..M).map(|i| eq_poly(&tc.beta_tau, i)).collect::>(); + let beta_eq_evals = (0..M) + .map(|i| eq_poly(&tc_red.beta_tau, i)) + .collect::>(); let eta = self .params .p - .evaluate_bundled(&beta_eq_evals, &tc.z) + .evaluate_bundled(&beta_eq_evals, &tc_out.z) .map_err(|_| ProverError::SpongeFish)?; - let nu_0 = tc.f.query_at_point(&tc.zeta_0); + let nu_0 = tc_out.f.query_at_point(&tc_red.zeta_0); - let (new_x, new_w) = tc.z.split_at(N - k); + let (new_x, new_w) = tc_out.z.split_at(N - k); let new_x = new_x.to_vec(); let new_w = new_w.to_vec(); - let new_beta = (vec![tc.beta_tau.clone()], vec![new_x]); + let new_beta = (vec![tc_red.beta_tau.clone()], vec![new_x]); let td = { let _s = tracing::info_span!("warp.commit_new_oracle").entered(); @@ -207,7 +220,7 @@ impl< MerkleTree::::new( &self.params.mt_leaf_hash_params, &self.params.mt_two_to_one_hash_params, - tc.f.evals().chunks(1).collect::>(), + tc_out.f.evals().chunks(1).collect::>(), )? }; let td_root_bytes: [u8; 32] = td @@ -220,65 +233,89 @@ impl< prover_state.prover_message(&nu_0); // Phase 3c: OOD — point queries on the oracle. - let ood_out = Ood { - oracle: &tc.f, - s: self.params.config.s, - log_n, - } - .prove(prover_state)?; + let ood_phase = Ood::::new(); + let (ood_red, _) = ood_phase.prove( + prover_state, + &OodStatement { + s: self.params.config.s, + log_n, + }, + (), + OodProverInputs { oracle: &tc_out.f }, + )?; // Sample shift queries — ordering-coupled to the batching ξ below. let queries = QueryIndices::::sample(prover_state, log_n, self.params.config.t); // Phase 3d: batching sumcheck. Assemble zetas = [ζ₀, ood_j…, query_k…]. - let ood_chunks: Vec<&[F]> = ood_out.samples_flat.chunks(log_n).collect(); - let mut zetas: Vec<&[F]> = + let mut zetas: Vec> = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); - zetas.push(tc.zeta_0.as_slice()); - zetas.extend(ood_chunks); - zetas.extend(queries.evaluation_points.iter().map(|v| v.as_slice())); - - let batching_out = Batching { - oracle: &tc.f, - zetas_prefix: &zetas, - s: self.params.config.s, - t: self.params.config.t, - log_n, + zetas.push(tc_red.zeta_0.clone()); + for chunk in ood_red.samples_flat.chunks(log_n) { + zetas.push(chunk.to_vec()); + } + for q in &queries.evaluation_points { + zetas.push(q.clone()); } - .prove(prover_state)?; + + let batching_phase = Batching::::new(); + let (batching_red, batching_out) = batching_phase.prove( + prover_state, + &BatchingStatement { + zetas_prefix: zetas, + s: self.params.config.s, + t: self.params.config.t, + log_n, + _phantom: PhantomData, + }, + (), + BatchingProverInputs { oracle: &tc_out.f }, + )?; // Phase 3e: proximity — index queries + auth paths on accumulated + // fresh oracles (NOT on the reduced `f`, which is this round's new // oracle). - let all_codewords: Vec> = acc_fs.into_iter().chain(pesat.codewords).collect(); - let prox = Proximity:: { - queries: &queries, - td_0: &pesat.td_0, - acc_td: &acc_tds, - all_codewords: &all_codewords, - } - .prove(prover_state)?; + let all_codewords: Vec> = acc_fs.into_iter().chain(pesat_out.codewords).collect(); + let proximity_phase = Proximity:: { + mt_leaf_hash_params: &self.params.mt_leaf_hash_params, + mt_two_to_one_hash_params: &self.params.mt_two_to_one_hash_params, + _phantom: PhantomData, + }; + let (_, prox) = proximity_phase.prove( + prover_state, + &ProximityStatement { + queries: queries.clone(), + l2, + t: self.params.config.t, + }, + (), + ProximityProverInputs { + td_0: &pesat_out.td_0, + acc_td: &acc_tds, + all_codewords: &all_codewords, + }, + )?; // Assemble new accumulator state and proof. let mut nus = Vec::with_capacity(1 + self.params.config.s); nus.push(nu_0); - nus.extend(ood_out.answers); + nus.extend(ood_red.answers); let new_acc_instance = AccumulatorInstance { rt: vec![td.root()], - alpha: vec![batching_out.alpha], + alpha: vec![batching_red.alpha], mu: vec![batching_out.mu], beta: new_beta, eta: vec![eta], }; let new_acc_witness = AccumulatorWitness { td: vec![td], - f: vec![tc.f.into_evals()], + f: vec![tc_out.f.into_evals()], w: vec![new_w], }; let proof = WARPProof { - rt_0: pesat.td_0.root(), - mu_i: pesat.mus, + rt_0: pesat_out.td_0.root(), + mu_i: pesat_red.mus, nu_0, nu_i: nus, auth_0: prox.auth_0, @@ -304,173 +341,157 @@ impl< let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); let n = self.params.code.code_len(); let log_n = log2(n) as usize; - let r = 1 + self.params.config.s + self.params.config.t; - let log_r = log2(r) as usize; - - // 1. Parse statement (fresh instances + accumulator). - let ( - l1_xs, - AccumulatorInstance { - rt: l2_roots, - alpha: l2_alphas, - mu: l2_mus, - beta: (l2_taus, l2_xs), - eta: l2_etas, - }, - ) = parse_statement::(verifier_state, l1, l2, N - k, log_n, log_m)?; - - // 2. Read PESAT phase: rt_0 + l1_mus, squeeze l1_taus, ω, τ. - let PreTwinConstraint { - rt_0, - l1_mus, - l1_taus, - omega, - tau, - } = derive_pre_twin_constraint::(verifier_state, l1, log_l, log_m)?; - - // 3. Twin-constraint sumcheck claim σ₁ = Σ_i τ_eq(i)·(μ_i + ω·η_i). - let tau_eq_evals = compute_hypercube_eq_evals(log_l, &tau); - let etas_l2_first = concat_slices(&l2_etas, &vec![F::zero(); l1]); - let sigma_1 = tau_eq_evals - .into_iter() - .zip( - l2_mus - .iter() - .copied() - .chain(l1_mus.iter().copied()) - .zip(etas_l2_first), - ) - .fold(F::zero(), |acc, (eq_tau, (mu, eta))| { - acc + eq_tau * (mu + omega * eta) - }); - - // 4. Run the twin-constraint sumcheck via effsc. The real oracle - // check is `final_claim == eq(τ, γ) · (ν₀ + ω·η)`, but ν₀ and η - // arrive on the transcript *after* the sumcheck rounds — so we - // capture `final_claim` in the closure and finish the check - // below after reading them. `sumcheck_verify` still enforces all - // per-round consistency (`q(0)+q(1)==claim`) automatically. - let tc_degree = 1 + (log_n + 1).max(log_m + 2); - let (gamma_sumcheck, tc_final_claim) = { - let mut wrap = EffscVerifierTranscript(verifier_state); - let res = sumcheck_verify(sigma_1, tc_degree, log_l, &mut wrap, |_, _| Ok(()))?; - (res.challenges, res.final_claim) + + // 1. Parse statement (fresh instances + accumulator from transcript). + // The acc_instance argument carries the new accumulator's α/β for the + // final consistency checks; the transcript-parsed `parsed_acc` carries + // the OLD l2 accumulator entries used during proximity / β-consistency. + let (l1_xs, parsed_acc) = + parse_statement::(verifier_state, l1, l2, N - k, log_n, log_m)?; + let acc_alpha_first = acc_instance.alpha[0].clone(); + let acc_beta_0_first = acc_instance.beta.0[0].clone(); + let acc_beta_1_first = acc_instance.beta.1[0].clone(); + let acc_mu_first = acc_instance.mu[0]; + let l2_roots = parsed_acc.rt.clone(); + let l2_taus = parsed_acc.beta.0.clone(); + let l2_xs = parsed_acc.beta.1.clone(); + + // 2. PESAT::verify — read rt_0, l1_mus; squeeze l1_taus. + let pesat_phase = Pesat:: { + code: &self.params.code, + mt_leaf_hash_params: &self.params.mt_leaf_hash_params, + mt_two_to_one_hash_params: &self.params.mt_two_to_one_hash_params, + _phantom: PhantomData, }; + let (pesat_red, pesat_v_out) = + pesat_phase.verify(verifier_state, &PesatStatement { l1, log_m }, ())?; + let l1_mus = pesat_red.mus; + let l1_taus = pesat_red.taus; + let rt_0 = pesat_v_out.rt_0; + + // 3. TwinConstraint::verify — squeeze ω, τ; run sumcheck. + let tc_phase = TwinConstraint:: { + r1cs: self.params.p.constraints(), + _phantom: PhantomData, + }; + let (tc_red, _) = tc_phase.verify( + verifier_state, + &TwinConstraintStatement { + acc_instance: parsed_acc, + l1_mus, + l1_taus: l1_taus.clone(), + log_l, + log_m, + log_n, + }, + (), + )?; + + // 4. Orchestrator-side reads BETWEEN twin-constraint and OOD: td, η, ν₀. + let _td_bytes: [u8; 32] = verifier_state.prover_message()?; + let eta: F = verifier_state.prover_message()?; + let nu_0: F = verifier_state.prover_message()?; + + // 5. Discharge the typed deferred oracle check from TwinConstraint. + // Equivalent to: final_claim ≟ eq(τ, γ) · (ν₀ + ω·η). + tc_red.deferred.discharge(&tc_red.gamma, nu_0, eta)?; - // 5. Read between-sumchecks state: td, η, ν₀, OOD, shift-query - // byte challenges, ξ. - let BetweenSumchecks { - td: _, - eta, - nus: mut nus_from_transcript, - ood_samples, - bytes_shift_queries, - xi, - } = derive_between_sumchecks::( + // 6. OOD::verify — squeeze samples; read answers. + let ood_phase = Ood::::new(); + let (ood_red, _) = ood_phase.verify( verifier_state, - log_n, - self.params.config.s, - self.params.config.t, + &OodStatement { + s: self.params.config.s, + log_n, + }, + (), )?; - // 6. Deferred twin-constraint oracle check. - (eq_poly_non_binary(&tau, &gamma_sumcheck) * (nus_from_transcript[0] + omega * eta) - == tc_final_claim) - .ok_or_err(VerifierError::Target)?; - - // 7. Proximity first: both the batching σ₂ and the batching oracle - // check consume `proof.shift_query_answers`, so we must reject - // malformed / tampered openings *before* feeding them into the - // sumcheck. Running proximity here preserves the existing - // negative-test error distinctions (`ShiftQuery`, `ShiftQueryIndex`, - // `NumShiftQueries`, `NumL2Instances`). + // 7. Orchestrator: squeeze shift-query byte challenges, build + // QueryIndices, run Proximity::verify. + let n_shift_query_bytes = (self.params.config.t * log_n).div_ceil(8); + let bytes_shift_queries: Vec = (0..n_shift_query_bytes) + .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) + .collect(); let queries: QueryIndices = QueryIndices::from_squeezed_bytes(&bytes_shift_queries, log_n, self.params.config.t); - ProximityVerify:: { - queries: &queries, - rt_0: &rt_0, - l2_roots: &l2_roots, - auth_0: &proof.auth_0, - auth_j: &proof.auth_j, - shift_query_answers: &proof.shift_query_answers, + let proximity_phase = Proximity:: { mt_leaf_hash_params: &self.params.mt_leaf_hash_params, mt_two_to_one_hash_params: &self.params.mt_two_to_one_hash_params, - l2, - t: self.params.config.t, - } - .verify(verifier_state)?; - - // 8. Derive everything needed to state the batching claim. - let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma_sumcheck); - let alpha_vecs = concat_slices(&l2_alphas, &vec![vec![F::zero(); log_n]; l1]); - let zeta_0 = scale_and_sum(&alpha_vecs, &gamma_eq_evals); - - // ν_{s+k}: inner product of the (now proximity-verified) shift-query - // answers with the γ-equality table. - let mut nu_s_t = vec![F::default(); self.params.config.t]; - for (i, v_jk) in proof.shift_query_answers.iter().enumerate() { - nu_s_t[i] = v_jk + _phantom: PhantomData, + }; + proximity_phase.verify( + verifier_state, + &ProximityStatement { + queries: queries.clone(), + l2, + t: self.params.config.t, + }, + ProximityVerifierInputs { + rt_0: &rt_0, + l2_roots: &l2_roots, + auth_0: &proof.auth_0, + auth_j: &proof.auth_j, + shift_query_answers: &proof.shift_query_answers, + _phantom: PhantomData, + }, + )?; + + // 8. Compute ν_{s+k} from the (now proximity-verified) shift answers + // and the γ-equality table; assemble the full nus vector. + let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &tc_red.gamma); + let mut nus = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); + nus.push(nu_0); + nus.extend(ood_red.answers); + for v_jk in proof.shift_query_answers.iter() { + let nu_st = v_jk .iter() .zip(&gamma_eq_evals) .fold(F::zero(), |acc, (v, eq)| acc + *eq * *v); + nus.push(nu_st); } - nus_from_transcript.extend(nu_s_t); - let nus = nus_from_transcript; - - let xi_eq_evals = compute_hypercube_eq_evals(log_r, &xi); - let sigma_2 = xi_eq_evals - .iter() - .zip(&nus) - .fold(F::zero(), |acc, (xi_eq, nu)| acc + *xi_eq * nu); - - // 8. Run the batching (inner-product) sumcheck via effsc. The - // oracle check `final_claim == μ · Σ_i eq(ζ_i, α)·ξ_eq(i)` is - // self-contained and runs inside the closure — a mismatch maps - // to `VerifierError::Target`. We still need the reduced α to - // compare against `acc_instance.alpha[0]` below, so the closure - // returns Ok via a side-channel capture of the zeta-eq sum. - // - // α arrives in MSB (sumcheck round) order; the prover stored - // the LSB-reversed form in `acc_instance.alpha[0]`, so we - // reverse once for MLE/eq consistency with arkworks. - let acc_mu = acc_instance.mu[0]; - let alpha_sumcheck_msb = { - let mut wrap = EffscVerifierTranscript(verifier_state); - let res = sumcheck_verify(sigma_2, 2, log_n, &mut wrap, |_, _| Ok(()))?; - let alpha_lsb: Vec = res.challenges.iter().rev().copied().collect(); - let mut zeta_eqs = Vec::with_capacity(r); - zeta_eqs.push(eq_poly_non_binary(&zeta_0, &alpha_lsb)); - for chunk in ood_samples.chunks(log_n) { - zeta_eqs.push(eq_poly_non_binary(chunk, &alpha_lsb)); - } - for pt in &queries.evaluation_points { - zeta_eqs.push(eq_poly_non_binary(pt, &alpha_lsb)); - } - debug_assert_eq!(zeta_eqs.len(), r); - let expected = acc_mu - * zeta_eqs - .into_iter() - .zip(&xi_eq_evals) - .fold(F::zero(), |acc, (a, b)| acc + a * *b); - (expected == res.final_claim).ok_or_err(VerifierError::Target)?; - res.challenges - }; + + // 9. Build zetas_prefix; Batching::verify squeezes ξ, computes σ₂, + // runs sumcheck, and does the final-claim oracle check. + let mut zetas: Vec> = + Vec::with_capacity(1 + self.params.config.s + self.params.config.t); + zetas.push(tc_red.zeta_0.clone()); + for chunk in ood_red.samples_flat.chunks(log_n) { + zetas.push(chunk.to_vec()); + } + for pt in &queries.evaluation_points { + zetas.push(pt.clone()); + } + + let batching_phase = Batching::::new(); + let (batching_red, _) = batching_phase.verify( + verifier_state, + &BatchingStatement { + zetas_prefix: zetas, + s: self.params.config.s, + t: self.params.config.t, + log_n, + _phantom: PhantomData, + }, + BatchingVerifierInputs { + nus, + acc_mu: acc_mu_first, + }, + )?; // 10. Accumulator consistency checks for the new code / circuit // evaluation points. - let alpha_sumcheck_lsb: Vec = alpha_sumcheck_msb.iter().rev().copied().collect(); - (acc_instance.alpha[0] == alpha_sumcheck_lsb) - .ok_or_err(VerifierError::CodeEvaluationPoint)?; + (acc_alpha_first == batching_red.alpha).ok_or_err(VerifierError::CodeEvaluationPoint)?; let betas = l2_taus .into_iter() .chain(l1_taus) - .zip(l2_xs.clone().into_iter().chain(l1_xs)) + .zip(l2_xs.into_iter().chain(l1_xs)) .map(|(tau_i, x)| concat_slices(&tau_i, &x)) .collect::>>(); let beta = scale_and_sum(&betas, &gamma_eq_evals); - let expected_beta = concat_slices(&acc_instance.beta.0[0], &acc_instance.beta.1[0]); + let expected_beta = concat_slices(&acc_beta_0_first, &acc_beta_1_first); (expected_beta == beta).ok_or_err(VerifierError::CircuitEvaluationPoint)?; Ok(()) diff --git a/src/protocol/phases/batching.rs b/src/protocol/phases/batching.rs index f48e886..bbdcda9 100644 --- a/src/protocol/phases/batching.rs +++ b/src/protocol/phases/batching.rs @@ -11,29 +11,41 @@ //! sumcheck, with the CBBZ23 / HyperPlonk sparse-evaluation optimization //! (`accumulate_sparse_evaluations`) folded in. //! -//! Takes the already-sampled shift-query evaluation points as input so the -//! transcript order (queries sampled, then ξ, then sumcheck messages) is -//! preserved. See the orchestrator in `src/lib.rs`. +//! IOR signature +//! ------------- +//! - `Statement` — `(zetas_prefix, s, t, log_n)` — shared. +//! - `Witness` — `()` +//! - `ProverInputs` — `&Oracle` (the committed oracle, full data) +//! - `VerifierInputs` — `(nus, acc_mu)` — used to compute `σ₂` and the +//! final-claim oracle check. +//! - `ReducedStatement` — `alpha` — the new code-eval point (LSB-indexed) +//! - `ProverOutputs` — `mu` — the prover's reported `\hat f(α)` +//! - `VerifierOutputs` — `()` use ark_ff::{Field, PrimeField}; use ark_std::log2; -use effsc::{noop_hook, provers::inner_product::InnerProductProver, runner::sumcheck}; -use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; +use effsc::{ + noop_hook, provers::inner_product::InnerProductProver, runner::sumcheck, + verifier::sumcheck_verify, +}; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::collections::HashMap; +use std::marker::PhantomData; use crate::count_ops; -use crate::error::ProverError; +use crate::error::{ProverError, VerifierError}; use crate::protocol::oracle::Oracle; -use crate::protocol::phases::ProverPhase; -use crate::utils::poly::eq_poly; +use crate::protocol::phases::IOR; +use crate::protocol::transcript::EffscVerifierTranscript; +use crate::utils::poly::{eq_poly, eq_poly_non_binary}; +use crate::BoolResult; /// [CBBZ23] / HyperPlonk sparse-evaluation optimization: for shift-query /// zetas (indices `1+s..r`), each ζ is a 0/1 vector representing a single -/// hypercube point. We accumulate the corresponding `eq_evals[i]` into a -/// sparse map keyed by that point's index. +/// hypercube point. fn accumulate_sparse_evaluations( - zetas: Vec<&[F]>, - eq_evals: Vec, + zetas: &[Vec], + eq_evals: &[F], s: usize, r: usize, ) -> HashMap { @@ -50,8 +62,7 @@ fn accumulate_sparse_evaluations( } /// Sum `dense_polys` column-wise and add the sparse contributions into the -/// resulting vector. Used to build the `g` side of the inner-product -/// sumcheck `∑_x f(x)·g(x)`. +/// resulting vector. fn batched_constraint_poly( dense_polys: &[Vec], sparse_polys: &HashMap, @@ -71,76 +82,113 @@ fn batched_constraint_poly( result } -/// Output of the batching sumcheck: the reduced point `α` and the target -/// `μ = \hat f(α)`. -pub struct BatchingOutput { +// ─── IOR signature types ────────────────────────────────────────────────── + +pub struct BatchingStatement { + /// `1 + s + t` evaluation points: `[ζ_0, ood_j…, query_k…]`. + pub zetas_prefix: Vec>, + pub s: usize, + pub t: usize, + pub log_n: usize, + pub _phantom: std::marker::PhantomData, +} + +pub struct BatchingProverInputs<'a, F: Field> { + pub oracle: &'a Oracle, +} + +pub struct BatchingVerifierInputs { + /// `1 + s + t` ν values; used to compute `σ₂ = Σ ξ_eq · ν`. + pub nus: Vec, + /// Multiplier on the final-claim oracle check. + pub acc_mu: F, +} + +pub struct BatchingReducedStatement { + /// New code-eval point (LSB-indexed). pub alpha: Vec, +} + +pub struct BatchingProverOutputs { + /// `\hat f(α)` — prover's report. pub mu: F, } -/// Batching sumcheck phase. -/// -/// `zetas_prefix` must contain `1 + s + t` evaluation points in the order -/// `[ζ_0, ood_0, ..., ood_{s-1}, query_0, ..., query_{t-1}]`. +/// Batching phase configuration. pub struct Batching<'a, F: Field> { - pub oracle: &'a Oracle, - pub zetas_prefix: &'a [&'a [F]], - pub s: usize, - pub t: usize, - pub log_n: usize, + pub _phantom: PhantomData<&'a F>, +} + +impl<'a, F: Field> Batching<'a, F> { + pub fn new() -> Self { + Self { + _phantom: PhantomData, + } + } +} + +impl<'a, F: Field> Default for Batching<'a, F> { + fn default() -> Self { + Self::new() + } } -impl<'a, F> ProverPhase for Batching<'a, F> +impl<'a, F> IOR for Batching<'a, F> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { - type Output = BatchingOutput; + type Statement = BatchingStatement; + type Witness = (); + type ProverInputs = BatchingProverInputs<'a, F>; + type VerifierInputs = BatchingVerifierInputs; + type ReducedStatement = BatchingReducedStatement; + type ProverOutputs = BatchingProverOutputs; + type VerifierOutputs = (); #[tracing::instrument( name = "batching", skip_all, - fields(s = self.s, t = self.t, log_n = self.log_n) + fields(s = statement.s, t = statement.t, log_n = statement.log_n) )] - fn prove(self, prover_state: &mut ProverState) -> Result { - let n = self.oracle.len(); - let r = 1 + self.s + self.t; + fn prove( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + _witness: Self::Witness, + inputs: Self::ProverInputs, + ) -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError> { + let n = inputs.oracle.len(); + let r = 1 + statement.s + statement.t; let log_r = log2(r) as usize; - debug_assert_eq!(self.zetas_prefix.len(), r); + debug_assert_eq!(statement.zetas_prefix.len(), r); let xis = prover_state.verifier_messages_vec::(log_r); - // compute evaluations for xi and the dense ood_evals_vec for the first 1+s zetas let (xi_eq_evals, ood_evals_vec) = { let _s = tracing::info_span!("batching.eq_evals").entered(); let xi_eq_evals = (0..r).map(|i| eq_poly(&xis, i)).collect::>(); - let ood_evals_vec = (0..1 + self.s) + let ood_evals_vec = (0..1 + statement.s) .map(|i| { (0..n) - .map(|a| eq_poly(self.zetas_prefix[i], a) * xi_eq_evals[i]) + .map(|a| eq_poly(&statement.zetas_prefix[i], a) * xi_eq_evals[i]) .collect::>() }) .collect::>(); (xi_eq_evals, ood_evals_vec) }; - // [CBBZ23] / HyperPlonk optimization for the t sparse shift-query zetas. let id_non_0_eval_sums = { let _s = tracing::info_span!("batching.accumulate_sparse").entered(); - accumulate_sparse_evaluations(self.zetas_prefix.to_vec(), xi_eq_evals, self.s, r) + accumulate_sparse_evaluations(&statement.zetas_prefix, &xi_eq_evals, statement.s, r) }; - // Run the inner-product sumcheck. `InnerProductProver` + `runner::sumcheck` - // is the new-style `SumcheckProver` entry point; wire format is three - // evaluations `[q(0), q(1), q(2)]` per round (`effsc::sumcheck_verify` - // reads them on the verifier side). The prover is MSB half-split, so the - // challenge vector arrives in MSB order — reverse once here so downstream - // MLE / eq_poly queries (arkworks' LSB-first convention) line up. + // Run the inner-product sumcheck. MSB half-split → reverse once. let alpha = { let _s = tracing::info_span!("batching.sumcheck").entered(); let log_n_bits = ark_std::log2(n) as u64; count_ops!(BatchingRounds, log_n_bits); let mut ip = InnerProductProver::new( - self.oracle.evals().to_vec(), + inputs.oracle.evals().to_vec(), batched_constraint_poly(&ood_evals_vec, &id_non_0_eval_sums), ); let mut challenges = @@ -149,8 +197,62 @@ where challenges }; - let mu = self.oracle.query_at_point(&alpha); + let mu = inputs.oracle.query_at_point(&alpha); + + Ok(( + BatchingReducedStatement { + alpha: alpha.clone(), + }, + BatchingProverOutputs { mu }, + )) + } + + #[tracing::instrument( + name = "batching.verify", + skip_all, + fields(s = statement.s, t = statement.t, log_n = statement.log_n) + )] + fn verify<'b>( + &self, + verifier_state: &mut VerifierState<'b>, + statement: &Self::Statement, + inputs: Self::VerifierInputs, + ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> { + let r = 1 + statement.s + statement.t; + let log_r = log2(r) as usize; + debug_assert_eq!(statement.zetas_prefix.len(), r); + debug_assert_eq!(inputs.nus.len(), r); + + // Squeeze ξ matching the prover. + let xis: Vec = (0..log_r) + .map(|_| verifier_state.verifier_message::()) + .collect(); + let xi_eq_evals = (0..r).map(|i| eq_poly(&xis, i)).collect::>(); + + // σ₂ = Σ ξ_eq · ν. + let sigma_2 = xi_eq_evals + .iter() + .zip(&inputs.nus) + .fold(F::zero(), |acc, (xi_eq, nu)| acc + *xi_eq * nu); + + // Run sumcheck_verify and check the final-claim oracle check. + let res = { + let mut wrap = EffscVerifierTranscript(verifier_state); + sumcheck_verify(sigma_2, 2, statement.log_n, &mut wrap, |_, _| Ok(()))? + }; + let alpha_lsb: Vec = res.challenges.iter().rev().copied().collect(); + + let mut zeta_eqs = Vec::with_capacity(r); + for zeta in &statement.zetas_prefix { + zeta_eqs.push(eq_poly_non_binary(zeta, &alpha_lsb)); + } + let expected = inputs.acc_mu + * zeta_eqs + .into_iter() + .zip(&xi_eq_evals) + .fold(F::zero(), |acc, (a, b)| acc + a * *b); + (expected == res.final_claim).ok_or_err(VerifierError::Target)?; - Ok(BatchingOutput { alpha, mu }) + Ok((BatchingReducedStatement { alpha: alpha_lsb }, ())) } } diff --git a/src/protocol/phases/mod.rs b/src/protocol/phases/mod.rs index 8d328f6..d1ed7fa 100644 --- a/src/protocol/phases/mod.rs +++ b/src/protocol/phases/mod.rs @@ -1,26 +1,24 @@ //! Warp IOR phases as first-class modules. //! -//! Paired spec: `docs/paper-mods/mod3_accumulator_state.tex` (forthcoming) — -//! the accumulator-as-state framing where each phase is a typed transition -//! in the protocol. +//! Paired spec: `docs/paper-mods/mod1_oracle.tex` (composition rule) and the +//! forthcoming `docs/paper-mods/mod3_accumulator_state.tex`. //! -//! Each submodule implements one IOR from the Warp construction. They share -//! a consistent shape, lifted into the [`ProverPhase`] / [`VerifierPhase`] -//! traits below: a phase is a struct that captures the static / borrowed -//! context it needs (codes, R1CS, merkle params, residues from upstream -//! phases), consumed by `prove` / `verify` along with a shared transcript -//! handle. +//! Each submodule implements one Interactive Oracle Reduction from the Warp +//! construction. The [`IOR`] trait below names the five paper-level +//! components — Statement, Witness, InputOracles, ReducedStatement, +//! OutputOracles — and splits oracle types into prover-side (full data) / +//! verifier-side (commitments) halves so the trait can serve both roles +//! against the same struct. //! -//! The top-level orchestrators in `src/lib.rs::WARP::prove` and `::verify` -//! thread state between phases by chaining `Phase::prove` / `Phase::verify` -//! calls — each phase's typed [`Output`](ProverPhase::Output) feeds the next -//! phase's struct. +//! Implementor pattern: a phase is a struct holding setup parameters +//! (codes, merkle hash params); `prove` and `verify` are `&self` methods +//! that take statement / witness / inputs per call and return reduced +//! statement + output oracles. //! -//! The traits cover what is genuinely uniform across phases (consume static -//! context + transcript → produce typed residue). Verifier-side phases like -//! "derive transcript randomness" that are pure plumbing are not wrapped in -//! `VerifierPhase` — they live in `src/protocol/transcript` and are called -//! directly from the orchestrator. +//! The top-level orchestrators in `src/lib.rs::WARP::prove` and `::verify` +//! thread state between phases by chaining `IOR::prove` / `IOR::verify` +//! calls — each phase's `ReducedStatement` and `Outputs` feed the next +//! phase's `Statement` / `Inputs`. pub mod batching; pub mod ood; @@ -32,26 +30,52 @@ use spongefish::{ProverState, VerifierState}; use crate::error::{ProverError, VerifierError}; -/// Prover-side IOR phase. +/// Interactive Oracle Reduction. /// -/// Implementors are structs whose fields hold the borrowed context the phase -/// needs. `prove` consumes the phase struct together with the shared -/// `ProverState`, returns a typed [`Output`](Self::Output) residue threaded -/// forward to downstream phases. -pub trait ProverPhase { - type Output; - fn prove(self, prover_state: &mut ProverState) -> Result; -} - -/// Verifier-side IOR phase. +/// Mirrors the IOR signature from `docs/paper-mods/mod1_oracle.tex` §4: /// -/// Mirrors [`ProverPhase`] for the verifier. Phases whose verifier counterpart -/// is purely transcript-derived (PESAT, OOD) skip this trait — the orchestrator -/// reads the relevant randomness via helpers in `src/protocol/transcript`. -pub trait VerifierPhase { - type Output; +/// ```text +/// (stmt, wit, oracles_in) --> (stmt', oracles_out) +/// ``` +/// +/// Seven associated types decompose the paper's tripartite split: +/// +/// - `Statement` / `ReducedStatement` are shared between prover and verifier +/// (what's claimed before and after the reduction). +/// - `Witness` is prover-only. +/// - Oracle types are split per role: the prover sees full evaluation data +/// (`ProverInputs` / `ProverOutputs`); the verifier sees commitments +/// (`VerifierInputs` / `VerifierOutputs`). For phases that don't pass any +/// oracle through one role, use `()`. +pub trait IOR { + /// Pre-reduction claim. Visible to prover and verifier. + type Statement; + /// Prover-only inputs (full witness data, etc.). + type Witness; + /// Oracles flowing in from upstream IORs (prover view: full data). + type ProverInputs; + /// Oracles flowing in from upstream IORs (verifier view: commitments). + type VerifierInputs; + /// Post-reduction claim. Visible to prover and verifier. + type ReducedStatement; + /// Oracles emitted by this IOR (prover view: full data, plus any private + /// reduced witness state). + type ProverOutputs; + /// Oracles emitted by this IOR (verifier view: commitments). + type VerifierOutputs; + + fn prove( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + witness: Self::Witness, + inputs: Self::ProverInputs, + ) -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError>; + fn verify<'a>( - self, + &self, verifier_state: &mut VerifierState<'a>, - ) -> Result; + statement: &Self::Statement, + inputs: Self::VerifierInputs, + ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError>; } diff --git a/src/protocol/phases/ood.rs b/src/protocol/phases/ood.rs index 7b56594..8aee302 100644 --- a/src/protocol/phases/ood.rs +++ b/src/protocol/phases/ood.rs @@ -4,50 +4,120 @@ //! composition of point queries on the committed oracle — see //! [`Oracle::query_at_point`](crate::protocol::oracle::Oracle::query_at_point). //! The verifier derives the same random points from the transcript. +//! +//! IOR signature +//! ------------- +//! - `Statement` — `(s, log_n)` +//! - `Witness` — `()` +//! - `ProverInputs` — `&Oracle` (the committed oracle, full data) +//! - `VerifierInputs` — `()` (the oracle check is deferred to the batching +//! sumcheck's final claim) +//! - `ReducedStatement` — `(samples_flat, answers)` — query points + their answers +//! - `ProverOutputs` — `()` +//! - `VerifierOutputs` — `()` use ark_ff::{Field, PrimeField}; -use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; +use std::marker::PhantomData; use crate::count_ops; -use crate::error::ProverError; +use crate::error::{ProverError, VerifierError}; use crate::protocol::oracle::Oracle; -use crate::protocol::phases::ProverPhase; +use crate::protocol::phases::IOR; + +pub struct OodStatement { + pub s: usize, + pub log_n: usize, +} -/// Output of the OOD phase: the flat challenge vector and the prover's -/// answers at each chunked evaluation point. -pub struct OodOutput { +pub struct OodProverInputs<'a, F: Field> { + pub oracle: &'a Oracle, +} + +pub struct OodReducedStatement { /// Flat challenge vector of length `s · log_n`. pub samples_flat: Vec, /// Answers `\hat f(ζ_j)` for each of the `s` chunked challenges. pub answers: Vec, } -/// OOD phase: sample `s` evaluation points, query the oracle at each, absorb -/// the answers. +/// OOD phase configuration. Stateless; the lifetime parameter exists only +/// to anchor `ProverInputs<'a>` for the trait impl. pub struct Ood<'a, F: Field> { - pub oracle: &'a Oracle, - pub s: usize, - pub log_n: usize, + pub _phantom: PhantomData<&'a F>, } -impl<'a, F> ProverPhase for Ood<'a, F> +impl<'a, F: Field> Ood<'a, F> { + pub fn new() -> Self { + Self { + _phantom: PhantomData, + } + } +} + +impl<'a, F: Field> Default for Ood<'a, F> { + fn default() -> Self { + Self::new() + } +} + +impl<'a, F> IOR for Ood<'a, F> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { - type Output = OodOutput; + type Statement = OodStatement; + type Witness = (); + type ProverInputs = OodProverInputs<'a, F>; + type VerifierInputs = (); + type ReducedStatement = OodReducedStatement; + type ProverOutputs = (); + type VerifierOutputs = (); - #[tracing::instrument(name = "ood", skip_all, fields(s = self.s, log_n = self.log_n))] - fn prove(self, prover_state: &mut ProverState) -> Result { - let samples_flat = prover_state.verifier_messages_vec::(self.s * self.log_n); - count_ops!(OodPointQueries, self.s as u64); + #[tracing::instrument(name = "ood", skip_all, fields(s = statement.s, log_n = statement.log_n))] + fn prove( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + _witness: Self::Witness, + inputs: Self::ProverInputs, + ) -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError> { + let samples_flat = prover_state.verifier_messages_vec::(statement.s * statement.log_n); + count_ops!(OodPointQueries, statement.s as u64); let answers = samples_flat - .chunks(self.log_n) - .map(|zeta| self.oracle.query_at_point(zeta)) + .chunks(statement.log_n) + .map(|zeta| inputs.oracle.query_at_point(zeta)) .collect::>(); prover_state.prover_messages(&answers); - Ok(OodOutput { - samples_flat, - answers, - }) + Ok(( + OodReducedStatement { + samples_flat, + answers, + }, + (), + )) + } + + #[tracing::instrument( + name = "ood.verify", + skip_all, + fields(s = statement.s, log_n = statement.log_n) + )] + fn verify<'b>( + &self, + verifier_state: &mut VerifierState<'b>, + statement: &Self::Statement, + _inputs: Self::VerifierInputs, + ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> { + let samples_flat: Vec = (0..statement.s * statement.log_n) + .map(|_| verifier_state.verifier_message::()) + .collect(); + let answers: Vec = verifier_state.prover_messages_vec(statement.s)?; + Ok(( + OodReducedStatement { + samples_flat, + answers, + }, + (), + )) } } diff --git a/src/protocol/phases/pesat.rs b/src/protocol/phases/pesat.rs index 0f43090..7f7b325 100644 --- a/src/protocol/phases/pesat.rs +++ b/src/protocol/phases/pesat.rs @@ -5,13 +5,15 @@ //! codewords, commit via an interleaved Merkle tree, absorb commitment and //! code evaluations, and derive the τ zero-check challenges. //! -//! Under Modification 1's oracle framing, this IOR has signature -//! `(statement, witnesses) -> (PesatOutput{codewords, commit, claims, τs})`; -//! downstream phases consume these as oracles. -//! -//! No verifier-side work lives here — PESAT emits oracles and τs that the -//! verifier derives afresh from the transcript via -//! `src/protocol/transcript/verifier.rs::derive_randomness`. +//! IOR signature +//! ------------- +//! - `Statement` — `(l1, log_m)` +//! - `Witness` — `&[Vec]` (fresh witnesses to encode) +//! - `ProverInputs` — `()` (PESAT is the source — no upstream oracles) +//! - `VerifierInputs` — `()` +//! - `ReducedStatement` — `(mus, taus)` — code-eval claims + zero-check randomness +//! - `ProverOutputs` — full codewords + Merkle tree (\(\Oracle{u}\) bundle) +//! - `VerifierOutputs` — Merkle root only use ark_codes::traits::LinearCode; use ark_crypto_primitives::{ @@ -19,16 +21,39 @@ use ark_crypto_primitives::{ merkle_tree::{Config, MerkleTree}, }; use ark_ff::{Field, PrimeField}; -use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; use crate::count_ops; use crate::crypto::merkle::build_codeword_leaves; -use crate::error::ProverError; -use crate::protocol::phases::ProverPhase; -use crate::types::PesatOutput; +use crate::error::{ProverError, VerifierError}; +use crate::protocol::phases::IOR; -/// PESAT phase: encode + commit + absorb + squeeze τ. +pub struct PesatStatement { + pub l1: usize, + pub log_m: usize, +} + +pub struct PesatWitness<'a, F: Field> { + pub witnesses: &'a [Vec], +} + +pub struct PesatReducedStatement { + pub mus: Vec, + pub taus: Vec>, +} + +pub struct PesatProverOutputs { + pub codewords: Vec>, + pub td_0: MerkleTree, +} + +pub struct PesatVerifierOutputs { + pub rt_0: MT::InnerDigest, +} + +/// PESAT phase configuration. Holds the linear code and merkle hash +/// parameters borrowed from the enclosing `WARP` struct. pub struct Pesat<'a, F, C, MT> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, @@ -38,31 +63,40 @@ where pub code: &'a C, pub mt_leaf_hash_params: &'a ::Parameters, pub mt_two_to_one_hash_params: &'a ::Parameters, - pub witnesses: &'a [Vec], - pub l1: usize, - pub log_m: usize, pub _phantom: PhantomData<(F, MT)>, } -impl<'a, F, C, MT> ProverPhase for Pesat<'a, F, C, MT> +impl<'a, F, C, MT> IOR for Pesat<'a, F, C, MT> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, C: LinearCode, MT: Config + From<[u8; 32]>>, { - type Output = PesatOutput; + type Statement = PesatStatement; + type Witness = PesatWitness<'a, F>; + type ProverInputs = (); + type VerifierInputs = (); + type ReducedStatement = PesatReducedStatement; + type ProverOutputs = PesatProverOutputs; + type VerifierOutputs = PesatVerifierOutputs; #[tracing::instrument( name = "pesat", skip_all, - fields(l1 = self.l1, log_m = self.log_m, n_witnesses = self.witnesses.len()) + fields(l1 = statement.l1, log_m = statement.log_m, n_witnesses = witness.witnesses.len()) )] - fn prove(self, prover_state: &mut ProverState) -> Result { + fn prove( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + witness: Self::Witness, + _inputs: Self::ProverInputs, + ) -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError> { // a. encode witnesses let (codewords, leaves) = { let _s = tracing::info_span!("pesat.encode").entered(); - count_ops!(EncodeCalls, self.witnesses.len() as u64); - build_codeword_leaves(self.code, self.witnesses, self.l1) + count_ops!(EncodeCalls, witness.witnesses.len() as u64); + build_codeword_leaves(self.code, witness.witnesses, statement.l1) }; // b. evaluation claims @@ -75,11 +109,11 @@ where MerkleTree::::new( self.mt_leaf_hash_params, self.mt_two_to_one_hash_params, - leaves.chunks_exact(self.l1).collect::>(), + leaves.chunks_exact(statement.l1).collect::>(), )? }; - // d. absorb commitment and code evaluations; e/f. derive τ challenges. + // d. absorb commitment + claims; e/f. derive τ challenges. let taus = { let _s = tracing::info_span!("pesat.absorb_and_derive").entered(); let root_bytes: [u8; 32] = td_0 @@ -90,16 +124,50 @@ where prover_state.prover_message(&root_bytes); prover_state.prover_messages(&mus); - (0..self.l1) - .map(|_| prover_state.verifier_messages_vec::(self.log_m)) + (0..statement.l1) + .map(|_| prover_state.verifier_messages_vec::(statement.log_m)) .collect::>() }; - Ok(PesatOutput { - codewords, - td_0, - mus, - taus, - }) + Ok(( + PesatReducedStatement { + mus: mus.clone(), + taus, + }, + PesatProverOutputs { codewords, td_0 }, + )) + } + + #[tracing::instrument( + name = "pesat.verify", + skip_all, + fields(l1 = statement.l1, log_m = statement.log_m) + )] + fn verify<'b>( + &self, + verifier_state: &mut VerifierState<'b>, + statement: &Self::Statement, + _inputs: Self::VerifierInputs, + ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> { + // commitment digest + let rt_0_bytes: [u8; 32] = verifier_state.prover_message()?; + let rt_0: MT::InnerDigest = rt_0_bytes.into(); + + // mus (l1 evaluation claims) + let mus: Vec = verifier_state.prover_messages_vec(statement.l1)?; + + // taus (l1 zero-check challenge vectors) + let taus: Vec> = (0..statement.l1) + .map(|_| { + (0..statement.log_m) + .map(|_| verifier_state.verifier_message::()) + .collect() + }) + .collect(); + + Ok(( + PesatReducedStatement { mus, taus }, + PesatVerifierOutputs { rt_0 }, + )) } } diff --git a/src/protocol/phases/proximity.rs b/src/protocol/phases/proximity.rs index 88a9542..f5887ff 100644 --- a/src/protocol/phases/proximity.rs +++ b/src/protocol/phases/proximity.rs @@ -11,6 +11,16 @@ //! with the transcript: it produces proof artifacts (auth paths + answers), //! which are then attached to the proof on the prover side and verified //! against transcript-derived commitments on the verifier side. +//! +//! IOR signature +//! ------------- +//! - `Statement` — `(queries, l2, t)` +//! - `Witness` — `()` +//! - `ProverInputs` — fresh + accumulated merkle trees + all codewords (full data) +//! - `VerifierInputs` — fresh + accumulated commitments + auth paths + answers +//! - `ReducedStatement` — `()` (Proximity is a check, not a reduction) +//! - `ProverOutputs` — auth paths + shift_query_answers (proof artifacts) +//! - `VerifierOutputs` — `()` use ark_crypto_primitives::{ crh::{CRHScheme, TwoToOneCRHScheme}, @@ -18,76 +28,108 @@ use ark_crypto_primitives::{ }; use ark_ff::Field; use spongefish::{ProverState, VerifierState}; +use std::marker::PhantomData; use crate::count_ops; use crate::crypto::merkle::compute_auth_paths; use crate::error::{ProverError, VerifierError}; -use crate::protocol::phases::{ProverPhase, VerifierPhase}; +use crate::protocol::phases::IOR; use crate::protocol::query::QueryIndices; use crate::BoolResult; -pub struct ProximityOutput { +pub struct ProximityStatement { + pub queries: QueryIndices, + pub l2: usize, + pub t: usize, +} + +pub struct ProximityProverInputs<'a, F: Field, MT: Config> { + pub td_0: &'a MerkleTree, + pub acc_td: &'a [MerkleTree], + pub all_codewords: &'a [Vec], +} + +pub struct ProximityVerifierInputs<'a, F: Field, MT: Config> { + pub rt_0: &'a MT::InnerDigest, + pub l2_roots: &'a [MT::InnerDigest], + pub auth_0: &'a [Path], + pub auth_j: &'a [Vec>], + pub shift_query_answers: &'a [Vec], + pub _phantom: PhantomData, +} + +pub struct ProximityProverOutputs { pub auth_0: Vec>, pub auth_j: Vec>>, pub shift_query_answers: Vec>, } -/// Proximity prover: open the queries against the fresh and accumulated -/// commitments, collect codeword values at each queried leaf. -/// -/// `all_codewords` must list the **accumulated** codewords first, then the -/// **fresh** PESAT codewords, matching the verifier's expectation at -/// `lib.rs::verify` (the `[l2..]` slice is the fresh chunk). +/// Proximity phase configuration. Holds borrowed merkle hash parameters used +/// by the verifier-side `verify` (the prover side doesn't need them — auth +/// paths are generated from the merkle trees passed in via `ProverInputs`). pub struct Proximity<'a, F: Field, MT: Config> { - pub queries: &'a QueryIndices, - pub td_0: &'a MerkleTree, - pub acc_td: &'a [MerkleTree], - pub all_codewords: &'a [Vec], + pub mt_leaf_hash_params: &'a ::Parameters, + pub mt_two_to_one_hash_params: &'a ::Parameters, + pub _phantom: PhantomData, } -impl<'a, F, MT> ProverPhase for Proximity<'a, F, MT> +impl<'a, F, MT> IOR for Proximity<'a, F, MT> where F: Field, - MT: Config, + MT: Config + 'a, + MT::InnerDigest: 'a, { - type Output = ProximityOutput; + type Statement = ProximityStatement; + type Witness = (); + type ProverInputs = ProximityProverInputs<'a, F, MT>; + type VerifierInputs = ProximityVerifierInputs<'a, F, MT>; + type ReducedStatement = (); + type ProverOutputs = ProximityProverOutputs; + type VerifierOutputs = (); #[tracing::instrument( name = "proximity", skip_all, fields( - n_queries = self.queries.leaf_positions.len(), - n_accumulators = self.acc_td.len(), - n_codewords = self.all_codewords.len(), + n_queries = statement.queries.leaf_positions.len(), + n_accumulators = inputs.acc_td.len(), + n_codewords = inputs.all_codewords.len(), ) )] - fn prove(self, _prover_state: &mut ProverState) -> Result { + fn prove( + &self, + _prover_state: &mut ProverState, + statement: &Self::Statement, + _witness: Self::Witness, + inputs: Self::ProverInputs, + ) -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError> { + let leaf_positions = &statement.queries.leaf_positions; + let auth_0 = { let _s = tracing::info_span!("proximity.auth_0").entered(); - count_ops!(MerklePathsGenerated, self.queries.leaf_positions.len() as u64); - compute_auth_paths(self.td_0, &self.queries.leaf_positions)? + count_ops!(MerklePathsGenerated, leaf_positions.len() as u64); + compute_auth_paths(inputs.td_0, leaf_positions)? }; let auth_j = { let _s = tracing::info_span!("proximity.auth_j").entered(); count_ops!( MerklePathsGenerated, - (self.acc_td.len() * self.queries.leaf_positions.len()) as u64 + (inputs.acc_td.len() * leaf_positions.len()) as u64 ); - self.acc_td + inputs + .acc_td .iter() - .map(|td| compute_auth_paths(td, &self.queries.leaf_positions)) + .map(|td| compute_auth_paths(td, leaf_positions)) .collect::>>, _>>()? }; let shift_query_answers = { let _s = tracing::info_span!("proximity.shift_queries").entered(); - let mut answers = vec![ - vec![F::default(); self.all_codewords.len()]; - self.queries.leaf_positions.len() - ]; - for (i, idx) in self.queries.leaf_positions.iter().enumerate() { - let row = self + let mut answers = + vec![vec![F::default(); inputs.all_codewords.len()]; leaf_positions.len()]; + for (i, idx) in leaf_positions.iter().enumerate() { + let row = inputs .all_codewords .iter() .map(|f| f[*idx]) @@ -97,84 +139,61 @@ where answers }; - Ok(ProximityOutput { - auth_0, - auth_j, - shift_query_answers, - }) + Ok(( + (), + ProximityProverOutputs { + auth_0, + auth_j, + shift_query_answers, + }, + )) } -} - -/// Proximity verifier: validate the auth paths against the rt_0 / l2_roots -/// commitments and check that the leaf claims line up. -/// -/// - `auth_0` opens the fresh PESAT tree at each query; expected leaves are -/// the `l2..` slice of each `shift_query_answers` row (the fresh chunk). -/// - `auth_j` opens each of `l2` accumulated trees at each query; expected -/// leaf for accumulator `i` at query `j` is `shift_query_answers[j][i]`. -pub struct ProximityVerify<'a, F: Field, MT: Config> { - pub queries: &'a QueryIndices, - pub rt_0: &'a MT::InnerDigest, - pub l2_roots: &'a [MT::InnerDigest], - pub auth_0: &'a [Path], - pub auth_j: &'a [Vec>], - pub shift_query_answers: &'a [Vec], - pub mt_leaf_hash_params: &'a ::Parameters, - pub mt_two_to_one_hash_params: &'a ::Parameters, - pub l2: usize, - pub t: usize, -} - -impl<'a, F, MT> VerifierPhase for ProximityVerify<'a, F, MT> -where - F: Field, - MT: Config, -{ - type Output = (); #[tracing::instrument( name = "proximity.verify", skip_all, - fields(t = self.t, l2 = self.l2) + fields(t = statement.t, l2 = statement.l2) )] fn verify<'b>( - self, + &self, _verifier_state: &mut VerifierState<'b>, - ) -> Result { - (self.shift_query_answers.len() == self.t).ok_or_err(VerifierError::NumShiftQueries)?; + statement: &Self::Statement, + inputs: Self::VerifierInputs, + ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> { + let leaf_positions = &statement.queries.leaf_positions; - for (i, path) in self.auth_0.iter().enumerate() { - (path.leaf_index == self.queries.leaf_positions[i]) - .ok_or_err(VerifierError::ShiftQueryIndex)?; + (inputs.shift_query_answers.len() == statement.t) + .ok_or_err(VerifierError::NumShiftQueries)?; + for (i, path) in inputs.auth_0.iter().enumerate() { + (path.leaf_index == leaf_positions[i]).ok_or_err(VerifierError::ShiftQueryIndex)?; count_ops!(MerklePathsVerified); let is_valid = path.verify( self.mt_leaf_hash_params, self.mt_two_to_one_hash_params, - self.rt_0, - &self.shift_query_answers[i][self.l2..], // leaves are evaluations of the l1 codewords + inputs.rt_0, + &inputs.shift_query_answers[i][statement.l2..], )?; is_valid.ok_or_err(VerifierError::ShiftQuery)?; } - (self.auth_j.len() == self.l2).ok_or_err(VerifierError::NumL2Instances)?; - for (i, paths) in self.auth_j.iter().enumerate() { - (paths.len() == self.t).ok_or_err(VerifierError::NumShiftQueries)?; - let root = &self.l2_roots[i]; + (inputs.auth_j.len() == statement.l2).ok_or_err(VerifierError::NumL2Instances)?; + for (i, paths) in inputs.auth_j.iter().enumerate() { + (paths.len() == statement.t).ok_or_err(VerifierError::NumShiftQueries)?; + let root = &inputs.l2_roots[i]; for (j, path) in paths.iter().enumerate() { - (path.leaf_index == self.queries.leaf_positions[j]) - .ok_or_err(VerifierError::ShiftQueryIndex)?; + (path.leaf_index == leaf_positions[j]).ok_or_err(VerifierError::ShiftQueryIndex)?; count_ops!(MerklePathsVerified); let is_valid = path.verify( self.mt_leaf_hash_params, self.mt_two_to_one_hash_params, root, - [self.shift_query_answers[j][i]], + [inputs.shift_query_answers[j][i]], )?; is_valid.ok_or_err(VerifierError::ShiftQuery)?; } } - Ok(()) + Ok(((), ())) } } diff --git a/src/protocol/phases/twin_constraint.rs b/src/protocol/phases/twin_constraint.rs index 7f05f0f..c787b24 100644 --- a/src/protocol/phases/twin_constraint.rs +++ b/src/protocol/phases/twin_constraint.rs @@ -16,23 +16,46 @@ //! - `t(X)` = linear interpolation of τ — equality polynomial //! //! Each round's round polynomial has the form `h(X) = (f(X) + ω·p(X))·t(X)`. +//! +//! IOR signature +//! ------------- +//! - `Statement` — accumulator instance + `l1_mus` + dimensions +//! - `Witness` — fresh witnesses + accumulator witness halves + R1CS +//! - `ProverInputs` — full codewords (from PESAT) + accumulated codewords +//! - `VerifierInputs` — `()` — the deferred oracle check (final\_claim ≟ eq(τ,γ)·(ν₀+ω·η)) +//! runs in the orchestrator after ν₀ and η arrive on the transcript +//! - `ReducedStatement` — sumcheck challenges γ + unchecked final_claim + ω, τ + new α (ζ₀) + new β_τ +//! - `ProverOutputs` — the new reduced oracle `f` + the reduced witness vector `z` +//! - `VerifierOutputs` — `()` (the new commitment is read from the transcript by the orchestrator) +use ark_crypto_primitives::merkle_tree::Config; use ark_ff::{Field, PrimeField}; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; use effsc::{ - coefficient_sumcheck::RoundPolyEvaluator, folding::protogalaxy, hypercube::Ascending, - noop_hook, provers::coefficient_lsb::CoefficientProverLSB, runner::sumcheck, + coefficient_sumcheck::RoundPolyEvaluator, + folding::protogalaxy, + hypercube::{compute_hypercube_eq_evals, Ascending}, + noop_hook, + provers::coefficient_lsb::CoefficientProverLSB, + runner::sumcheck, + verifier::sumcheck_verify, }; -use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; +use std::marker::PhantomData; use crate::count_ops; -use crate::error::ProverError; +use crate::error::{ProverError, VerifierError}; use crate::protocol::oracle::Oracle; -use crate::protocol::phases::ProverPhase; +use crate::protocol::phases::IOR; +use crate::protocol::transcript::EffscVerifierTranscript; use crate::relations::r1cs::R1CSConstraints; use crate::types::AccumulatorInstance; -use crate::utils::{concat_slices, poly::eq_poly}; -use ark_crypto_primitives::merkle_tree::Config; +use crate::utils::{ + concat_slices, + poly::{eq_poly, eq_poly_non_binary}, + scale_and_sum, +}; +use crate::BoolResult; /// Degree-1 polynomial interpolating two field elements: `lo + (hi - lo)·X`. fn linear_poly(lo: F, hi: F) -> DensePolynomial { @@ -65,8 +88,7 @@ fn eval_r1cs_constraint_poly( } /// Round-polynomial evaluator fusing α-fold, β-fold, and τ-linear into a -/// single sumcheck pass. See `mod2_structured_sumcheck.tex` (stub) — the -/// fusion will be promoted to a paper-level primitive in Plan B'. +/// single sumcheck pass. struct TwinConstraintEvaluator<'a, F: Field> { r1cs: &'a R1CSConstraints, omega: F, @@ -149,72 +171,122 @@ impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { } } -/// Output of the twin-constraint sumcheck. All oracles / vectors are the -/// reduced claim state consumed by downstream phases (OOD, batching, final -/// target). -pub struct TwinConstraintOutput { - /// Reduced codeword oracle `f` — consumed by OOD, batching, proximity. +// ─── IOR signature types ────────────────────────────────────────────────── + +pub struct TwinConstraintStatement { + pub acc_instance: AccumulatorInstance, + pub l1_mus: Vec, + pub l1_taus: Vec>, + pub log_l: usize, + pub log_m: usize, + pub log_n: usize, +} + +pub struct TwinConstraintWitness<'a, F: Field> { + pub acc_witness_w: &'a [Vec], + pub instances: &'a [Vec], + pub witnesses: &'a [Vec], +} + +pub struct TwinConstraintProverInputs<'a, F: Field> { + /// Fresh codewords emitted by PESAT. + pub fresh_codewords: &'a [Vec], + /// Accumulated codewords (from the accumulator witness). + pub acc_codewords: &'a [Vec], +} + +/// A typed "you owe me a check" handle. +/// +/// The TwinConstraint sumcheck reduces σ₁ to a sumcheck final value, but the +/// actual oracle check `final_claim ≟ eq(τ, γ) · (ν₀ + ω·η)` cannot be +/// completed inside `TwinConstraint::verify` because ν₀ and η arrive on the +/// transcript *after* the sumcheck rounds. Rather than splitting +/// TwinConstraint into two IORs (which cascades into other phases having +/// similar shapes), we expose the obligation as a typed value. +/// +/// Discharge by calling [`Self::discharge`] with the missing inputs once the +/// orchestrator has read them from the transcript. +pub struct DeferredOracleCheck { + /// Zero-check randomness ω squeezed at TwinConstraint entry. + pub omega: F, + /// Zero-check challenge τ squeezed at TwinConstraint entry. + pub tau: Vec, + /// Sumcheck final value — what the orchestrator must verify against + /// `eq(τ, γ) · (ν₀ + ω·η)`. + pub claim: F, +} + +impl DeferredOracleCheck { + /// Discharge the deferred check. + /// + /// Returns `Ok(())` iff `eq(τ, γ) · (ν₀ + ω·η) == claim`. `γ` arrives via + /// the parent `TwinConstraintReducedStatement`; `ν₀, η` come from the + /// transcript segment immediately following the TwinConstraint sumcheck. + pub fn discharge(&self, gamma: &[F], nu_0: F, eta: F) -> Result<(), VerifierError> { + let expected = eq_poly_non_binary(&self.tau, gamma) * (nu_0 + self.omega * eta); + (expected == self.claim).ok_or_err(VerifierError::Target) + } +} + +pub struct TwinConstraintReducedStatement { + /// Sumcheck challenge vector (LSB-indexed). + pub gamma: Vec, + /// New code-evaluation point (becomes the new accumulator's α). + pub zeta_0: Vec, + /// Reduced τ point (becomes the τ component of the new accumulator's β). + pub beta_tau: Vec, + /// Typed obligation: the sumcheck's final value awaits verification + /// against `(ν₀, η)` arriving on the transcript next. Call + /// [`DeferredOracleCheck::discharge`] from the orchestrator. + pub deferred: DeferredOracleCheck, +} + +pub struct TwinConstraintProverOutputs { + /// New reduced codeword oracle. pub f: Oracle, /// Reduced witness vector `z = (x, w)` — consumed by η evaluation. pub z: Vec, - /// New code evaluation point `ζ₀` — becomes the new accumulator α. - pub zeta_0: Vec, - /// Reduced τ — becomes the τ component of the new accumulator β. - pub beta_tau: Vec, } -/// Twin-constraint sumcheck phase: fold τ-zero-check + α-codeword check + -/// β-R1CS check into a single coefficient-form sumcheck. -/// -/// Takes codeword slices by reference so the caller (orchestrator) can hand -/// them to [`Proximity`](super::proximity::Proximity) after this phase -/// returns. `fresh_taus` and `acc_instance` are consumed into the sumcheck -/// tables — neither is needed downstream. +/// TwinConstraint phase configuration. pub struct TwinConstraint<'a, F, MT> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, MT: Config + From<[u8; 32]>>, { - pub fresh_codewords: &'a [Vec], - pub fresh_taus: Vec>, - pub acc_instance: AccumulatorInstance, - pub acc_witness_f: &'a [Vec], - pub acc_witness_w: &'a [Vec], - pub instances: &'a [Vec], - pub witnesses: &'a [Vec], pub r1cs: &'a R1CSConstraints, - pub log_l: usize, - pub log_m: usize, - pub log_n: usize, + pub _phantom: PhantomData, } -impl<'a, F, MT> ProverPhase for TwinConstraint<'a, F, MT> +impl<'a, F, MT> IOR for TwinConstraint<'a, F, MT> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, MT: Config + From<[u8; 32]>>, { - type Output = TwinConstraintOutput; + type Statement = TwinConstraintStatement; + type Witness = TwinConstraintWitness<'a, F>; + type ProverInputs = TwinConstraintProverInputs<'a, F>; + type VerifierInputs = (); + type ReducedStatement = TwinConstraintReducedStatement; + type ProverOutputs = TwinConstraintProverOutputs; + type VerifierOutputs = (); #[tracing::instrument( name = "twin_constraint", skip_all, - fields(log_l = self.log_l, log_m = self.log_m, log_n = self.log_n) + fields(log_l = statement.log_l, log_m = statement.log_m, log_n = statement.log_n) )] - fn prove(self, prover_state: &mut ProverState) -> Result { - let TwinConstraint { - fresh_codewords, - fresh_taus, - acc_instance, - acc_witness_f, - acc_witness_w, - instances, - witnesses, - r1cs, - log_l, - log_m, - log_n, - } = self; - let l1 = fresh_codewords.len(); + fn prove( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + witness: Self::Witness, + inputs: Self::ProverInputs, + ) -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError> { + let l1 = inputs.fresh_codewords.len(); + let log_l = statement.log_l; + let log_m = statement.log_m; + let log_n = statement.log_n; // a. zero-check randomness let omega: F = prover_state.verifier_message(); @@ -225,45 +297,54 @@ where .map(|p| eq_poly(&tau, p.index)) .collect::>(); - let alpha_vecs = concat_slices(&acc_instance.alpha, &vec![vec![F::zero(); log_n]; l1]); + let alpha_vecs = concat_slices( + &statement.acc_instance.alpha, + &vec![vec![F::zero(); log_n]; l1], + ); - let z_vecs: Vec> = acc_instance + let z_vecs: Vec> = statement + .acc_instance .beta .1 .iter() - .zip(acc_witness_w) - .chain(instances.iter().zip(witnesses)) + .zip(witness.acc_witness_w) + .chain(witness.instances.iter().zip(witness.witnesses)) .map(|(x, w)| concat_slices(x, w)) .collect(); - let beta_vecs: Vec> = acc_instance.beta.0.into_iter().chain(fresh_taus).collect(); + // β tables: accumulated β-τs first, then PESAT τs. + let beta_vecs: Vec> = statement + .acc_instance + .beta + .0 + .iter() + .cloned() + .chain(statement.l1_taus.iter().cloned()) + .collect(); let tablewise = vec![ - concat_slices(acc_witness_f, fresh_codewords), // u - z_vecs, // z - alpha_vecs, // a - beta_vecs, // b + concat_slices(inputs.acc_codewords, inputs.fresh_codewords), // u + z_vecs, // z + alpha_vecs, // a + beta_vecs, // b ]; let pw = vec![tau_eq_evals]; // tau let degree = 1 + (log_n + 1).max(log_m + 2); let evaluator = TwinConstraintEvaluator { - r1cs, + r1cs: self.r1cs, omega, degree, }; - // c. run the sumcheck. `CoefficientProverLSB` + `runner::sumcheck` is - // the new-style `SumcheckProver` entry point; wire format is `d+1` - // evaluations per round (the verifier uses `effsc::sumcheck_verify` on - // the other side). + // c. run the sumcheck. let mut cc = CoefficientProverLSB::new(&evaluator, tablewise, pw); - { + let proof = { let _s = tracing::info_span!("twin_constraint.sumcheck").entered(); count_ops!(TwinConstraintRounds, log_l as u64); - let proof = sumcheck(&mut cc, log_l, prover_state, noop_hook); - debug_assert_eq!(proof.challenges.len(), log_l); - } + sumcheck(&mut cc, log_l, prover_state, noop_hook) + }; + debug_assert_eq!(proof.challenges.len(), log_l); // d. pull the single remaining row out of each tablewise table. let reduced = cc.tablewise(); @@ -273,11 +354,113 @@ where let zeta_0 = reduced[2][0].clone(); let beta_tau = reduced[3][0].clone(); - Ok(TwinConstraintOutput { - f: Oracle::from_evals(f), - z, - zeta_0, - beta_tau, - }) + let final_claim = proof.final_value; + + Ok(( + TwinConstraintReducedStatement { + gamma: proof.challenges, + zeta_0, + beta_tau, + deferred: DeferredOracleCheck { + omega, + tau, + claim: final_claim, + }, + }, + TwinConstraintProverOutputs { + f: Oracle::from_evals(f), + z, + }, + )) + } + + #[tracing::instrument( + name = "twin_constraint.verify", + skip_all, + fields(log_l = statement.log_l, log_m = statement.log_m, log_n = statement.log_n) + )] + fn verify<'b>( + &self, + verifier_state: &mut VerifierState<'b>, + statement: &Self::Statement, + _inputs: Self::VerifierInputs, + ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> { + let log_l = statement.log_l; + let log_n = statement.log_n; + let l1 = statement.l1_mus.len(); + let l2 = statement.acc_instance.mu.len(); + + // Squeeze ω, τ matching the prover. + let omega: F = verifier_state.verifier_message(); + let tau: Vec = (0..log_l) + .map(|_| verifier_state.verifier_message::()) + .collect(); + + // Compute σ₁ = Σ_i τ_eq(i) · (μ_i + ω · η_i). + let tau_eq_evals = compute_hypercube_eq_evals(log_l, &tau); + let etas_l2_first = concat_slices(&statement.acc_instance.eta, &vec![F::zero(); l1]); + let sigma_1 = tau_eq_evals + .into_iter() + .zip( + statement + .acc_instance + .mu + .iter() + .copied() + .chain(statement.l1_mus.iter().copied()) + .zip(etas_l2_first), + ) + .fold(F::zero(), |acc, (eq_tau, (mu, eta))| { + acc + eq_tau * (mu + omega * eta) + }); + + // Run the sumcheck. The deferred oracle check + // final_claim == eq(τ, γ) · (ν₀ + ω · η) + // is left to the orchestrator because ν₀ and η arrive on the + // transcript AFTER the sumcheck rounds (between TwinConstraint and + // OOD). We surface the unchecked final_claim and ω, τ, γ via + // ReducedStatement so the orchestrator can finish the check. + let tc_degree = 1 + (log_n + 1).max(statement.log_m + 2); + let (gamma, final_claim) = { + let mut wrap = EffscVerifierTranscript(verifier_state); + let res = sumcheck_verify(sigma_1, tc_degree, log_l, &mut wrap, |_, _| Ok(()))?; + (res.challenges, res.final_claim) + }; + + // Compute ζ₀ and β_τ from the sumcheck challenges (mirror the prover's + // sumcheck-fold of α and β). + let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma); + let alpha_vecs = concat_slices( + &statement.acc_instance.alpha, + &vec![vec![F::zero(); log_n]; l1], + ); + let zeta_0 = scale_and_sum(&alpha_vecs, &gamma_eq_evals); + + // β τ-vectors: accumulated taus first (length l2), then PESAT taus + // (length l1). We need the τ component of the new β = sum_{i} γ_eq(i) · β_i. + let beta_taus: Vec> = statement + .acc_instance + .beta + .0 + .iter() + .cloned() + .chain(statement.l1_taus.iter().cloned()) + .collect(); + debug_assert_eq!(beta_taus.len(), l2 + l1); + let beta_tau = scale_and_sum(&beta_taus, &gamma_eq_evals); + + Ok(( + TwinConstraintReducedStatement { + gamma, + zeta_0, + beta_tau, + deferred: DeferredOracleCheck { + omega, + tau, + claim: final_claim, + }, + }, + (), + )) } } diff --git a/src/protocol/query.rs b/src/protocol/query.rs index 1921af9..bbe207f 100644 --- a/src/protocol/query.rs +++ b/src/protocol/query.rs @@ -1,6 +1,7 @@ use ark_ff::Field; use spongefish::ProverState; +#[derive(Clone)] pub struct QueryIndices { pub leaf_positions: Vec, // for merkle tree lookups pub evaluation_points: Vec>, // for eq polynomial evals diff --git a/texput.log b/texput.log new file mode 100644 index 0000000..438b14a --- /dev/null +++ b/texput.log @@ -0,0 +1,20 @@ +This is LuaHBTeX, Version 1.16.0 (TeX Live 2023) (format=lualatex 2024.1.16) 3 MAY 2026 20:03 + restricted system commands enabled. +**iors.tex + +! Emergency stop. +<*> iors.tex + +*** (job aborted, file error in nonstop mode) + + + +Here is how much of LuaTeX's memory you used: + 5 strings out of 478285 + 100000,1977958 words of node,token memory allocated 270 words of node memory still in use: + 1 hlist, 39 glue_spec nodes + avail lists: 2:12,3:3,4:1,5:1 + 20331 multiletter control sequences out of 65536+600000 + 14 fonts using 591679 bytes + 0i,0n,0p,0b,6s stack positions out of 10000i,1000n,20000p,200000b,200000s +! ==> Fatal error occurred, no output PDF file produced! From 391c8e82f73260d80d4b566f82bf8c70d998eca5 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Mon, 4 May 2026 20:30:25 +0200 Subject: [PATCH 24/68] integrate ark-vc --- Cargo.toml | 2 + benches/warp_rs.rs | 7 +- docs/paper-mods/slides/iors.aux | 53 - docs/paper-mods/slides/iors.log | 1855 ------------------------ docs/paper-mods/slides/iors.nav | 37 - docs/paper-mods/slides/iors.out | 0 docs/paper-mods/slides/iors.pdf | Bin 128936 -> 299317 bytes docs/paper-mods/slides/iors.snm | 0 docs/paper-mods/slides/iors.tex | 38 +- docs/paper-mods/slides/iors.toc | 0 docs/paper-mods/slides/iors.vrb | 22 - src/crypto/merkle/mod.rs | 69 +- src/lib.rs | 208 ++- src/protocol/phases/pesat.rs | 73 +- src/protocol/phases/proximity.rs | 214 +-- src/protocol/phases/twin_constraint.rs | 18 +- src/protocol/transcript/prover.rs | 14 +- src/protocol/transcript/verifier.rs | 145 +- src/serialize.rs | 61 +- src/traits.rs | 18 +- src/types.rs | 107 +- tests/integration_warp.rs | 38 +- tests/profile_json.rs | 7 +- tests/verifier_negative.rs | 49 +- 24 files changed, 498 insertions(+), 2537 deletions(-) delete mode 100644 docs/paper-mods/slides/iors.aux delete mode 100644 docs/paper-mods/slides/iors.log delete mode 100644 docs/paper-mods/slides/iors.nav delete mode 100644 docs/paper-mods/slides/iors.out delete mode 100644 docs/paper-mods/slides/iors.snm delete mode 100644 docs/paper-mods/slides/iors.toc delete mode 100644 docs/paper-mods/slides/iors.vrb diff --git a/Cargo.toml b/Cargo.toml index 5ea082b..19b58f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,8 @@ tracing = { version = "0.1", default-features = false, features = ["std", "attri tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter", "json", "registry"], optional = true } libc = { version = "0.2", optional = true } ark-codes = { git = "https://github.com/z-tech/ark-codes.git", branch = "z-tech/arkworks-0.6.0" } +ark-vc = { path = "../ark_vc/crates/ark-vc" } +ark-mt = { path = "../ark_vc/crates/ark-mt", features = ["blake3", "blake3-field"] } [dev-dependencies] ark-bls12-381 = "0.6.0" diff --git a/benches/warp_rs.rs b/benches/warp_rs.rs index 2f238b8..a4e1a55 100644 --- a/benches/warp_rs.rs +++ b/benches/warp_rs.rs @@ -2,7 +2,7 @@ use ark_codes::reed_solomon::config::ReedSolomonConfig; use ark_codes::reed_solomon::ReedSolomon; use ark_codes::traits::LinearCode; -use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; +use ark_mt::blake3::Blake3FieldHasher; use ark_std::rand::thread_rng; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use utils::domainsep::init_prover_state; @@ -32,12 +32,11 @@ pub fn bench_rs_warp_fields(c: &mut Criterion) { for l in [32, 64, 128, 256, 512] { let warp_config = WARPConfig::new(l, l, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = WARP::<_, _, _, Blake3MerkleConfig<_>>::new( + let hash_chain_warp = WARP::<_, _, _, Blake3FieldHasher>::new( warp_config.clone(), code.clone(), r1cs.clone(), - (), - (), + Blake3FieldHasher::::new(), ); let instances_witnesses = diff --git a/docs/paper-mods/slides/iors.aux b/docs/paper-mods/slides/iors.aux deleted file mode 100644 index f70d6f4..0000000 --- a/docs/paper-mods/slides/iors.aux +++ /dev/null @@ -1,53 +0,0 @@ -\relax -\providecommand\hyper@newdestlabel[2]{} -\providecommand\HyperFirstAtBeginDocument{\AtBeginDocument} -\HyperFirstAtBeginDocument{\ifx\hyper@anchor\@undefined -\global\let\oldnewlabel\newlabel -\gdef\newlabel#1#2{\newlabelxx{#1}#2} -\gdef\newlabelxx#1#2#3#4#5#6{\oldnewlabel{#1}{{#2}{#3}}} -\AtEndDocument{\ifx\hyper@anchor\@undefined -\let\newlabel\oldnewlabel -\fi} -\fi} -\global\let\hyper@last\relax -\gdef\HyperFirstAtBeginDocument#1{#1} -\providecommand\HyField@AuxAddToFields[1]{} -\providecommand\HyField@AuxAddToCoFields[2]{} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{1}{1/1}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {1}{1}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{2}{2/2}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {2}{2}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{3}{3/3}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {3}{3}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{4}{4/4}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {4}{4}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{5}{5/5}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {5}{5}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{6}{6/6}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {6}{6}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{7}{7/7}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {7}{7}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{8}{8/8}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {8}{8}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{9}{9/9}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {9}{9}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{10}{10/10}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {10}{10}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{11}{11/11}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {11}{11}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{12}{12/12}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {12}{12}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{13}{13/13}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {13}{13}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{14}{14/14}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {14}{14}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{15}{15/15}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {15}{15}}} -\@writefile{nav}{\headcommand {\slideentry {0}{0}{16}{16/16}{}{0}}} -\@writefile{nav}{\headcommand {\beamer@framepages {16}{16}}} -\@writefile{nav}{\headcommand {\beamer@partpages {1}{16}}} -\@writefile{nav}{\headcommand {\beamer@subsectionpages {1}{16}}} -\@writefile{nav}{\headcommand {\beamer@sectionpages {1}{16}}} -\@writefile{nav}{\headcommand {\beamer@documentpages {16}}} -\@writefile{nav}{\headcommand {\gdef \inserttotalframenumber {15}}} -\gdef \@abspage@last{16} diff --git a/docs/paper-mods/slides/iors.log b/docs/paper-mods/slides/iors.log deleted file mode 100644 index e2215ff..0000000 --- a/docs/paper-mods/slides/iors.log +++ /dev/null @@ -1,1855 +0,0 @@ -This is LuaHBTeX, Version 1.16.0 (TeX Live 2023) (format=lualatex 2024.1.16) 3 MAY 2026 20:04 - restricted system commands enabled. -**iors.tex -(./iors.tex -LaTeX2e <2022-11-01> patch level 1 -Lua module: luaotfload 2022-10-03 3.23 Lua based OpenType font support -Lua module: lualibs 2022-10-04 2.75 ConTeXt Lua standard libraries. -Lua module: lualibs-extended 2022-10-04 2.75 ConTeXt Lua libraries -- extended c -ollection. -luaotfload | conf : Root cache directory is "/Users/zitek/Library/texlive/2023/t -exmf-var/luatex-cache/generic/names". -luaotfload | init : Loading fontloader "fontloader-2022-10-03.lua" from kpse-res -olved path "/usr/local/texlive/2023/texmf-dist/tex/luatex/luaotfload/fontloader- -2022-10-03.lua". -Lua-only attribute luaotfload@noligature = 1 -luaotfload | init : Context OpenType loader version 3.120 -Inserting `luaotfload.node_processor' in `pre_linebreak_filter'. -Inserting `luaotfload.node_processor' in `hpack_filter'. -Inserting `luaotfload.glyph_stream' in `glyph_stream_provider'. -Inserting `luaotfload.define_font' in `define_font'. -Lua-only attribute luaotfload_color_attribute = 2 -luaotfload | conf : Root cache directory is "/Users/zitek/Library/texlive/2023/t -exmf-var/luatex-cache/generic/names". -Inserting `luaotfload.harf.strip_prefix' in `find_opentype_file'. -Inserting `luaotfload.harf.strip_prefix' in `find_truetype_file'. -Removing `luaotfload.glyph_stream' from `glyph_stream_provider'. -Inserting `luaotfload.harf.glyphstream' in `glyph_stream_provider'. -Inserting `luaotfload.harf.finalize_vlist' in `post_linebreak_filter'. -Inserting `luaotfload.harf.finalize_hlist' in `hpack_filter'. -Inserting `luaotfload.cleanup_files' in `wrapup_run'. -Inserting `luaotfload.harf.finalize_unicode' in `finish_pdffile'. -Inserting `luaotfload.glyphinfo' in `glyph_info'. -Lua-only attribute luaotfload.letterspace_done = 3 -Inserting `luaotfload.aux.set_sscale_dimens' in `luaotfload.patch_font'. -Inserting `luaotfload.aux.set_font_index' in `luaotfload.patch_font'. -Inserting `luaotfload.aux.patch_cambria_domh' in `luaotfload.patch_font'. -Inserting `luaotfload.aux.fixup_fontdata' in `luaotfload.patch_font_unsafe'. -Inserting `luaotfload.aux.set_capheight' in `luaotfload.patch_font'. -Inserting `luaotfload.aux.set_xheight' in `luaotfload.patch_font'. -Inserting `luaotfload.rewrite_fontname' in `luaotfload.patch_font'. L3 programm -ing layer <2023-02-22> -Inserting `tracingstacklevels' in `input_level_string'. -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamer.cls -Document Class: beamer 2023/02/20 v3.69 A class for typesetting presentations -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasemodes.sty -(/usr/local/texlive/2023/texmf-dist/tex/latex/etoolbox/etoolbox.sty -Package: etoolbox 2020/10/05 v2.5k e-TeX tools for LaTeX (JAW) -\etb@tempcnta=\count183 -) -\beamer@tempbox=\box51 -\beamer@tempcount=\count184 -\c@beamerpauses=\count185 - -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasedecode.sty -\beamer@slideinframe=\count186 -\beamer@minimum=\count187 -\beamer@decode@box=\box52 -) -\beamer@commentbox=\box53 -\beamer@modecount=\count188 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/iftex/iftex.sty -Package: iftex 2022/02/03 v1.0f TeX engine tests -) -\headdp=\dimen139 -\footheight=\dimen140 -\sidebarheight=\dimen141 -\beamer@tempdim=\dimen142 -\beamer@finalheight=\dimen143 -\beamer@animht=\dimen144 -\beamer@animdp=\dimen145 -\beamer@animwd=\dimen146 -\beamer@leftmargin=\dimen147 -\beamer@rightmargin=\dimen148 -\beamer@leftsidebar=\dimen149 -\beamer@rightsidebar=\dimen150 -\beamer@boxsize=\dimen151 -\beamer@vboxoffset=\dimen152 -\beamer@descdefault=\dimen153 -\beamer@descriptionwidth=\dimen154 -\beamer@lastskip=\skip48 -\beamer@areabox=\box54 -\beamer@animcurrent=\box55 -\beamer@animshowbox=\box56 -\beamer@sectionbox=\box57 -\beamer@logobox=\box58 -\beamer@linebox=\box59 -\beamer@sectioncount=\count189 -\beamer@subsubsectionmax=\count190 -\beamer@subsectionmax=\count191 -\beamer@sectionmax=\count192 -\beamer@totalheads=\count193 -\beamer@headcounter=\count194 -\beamer@partstartpage=\count195 -\beamer@sectionstartpage=\count196 -\beamer@subsectionstartpage=\count197 -\beamer@animationtempa=\count198 -\beamer@animationtempb=\count199 -\beamer@xpos=\count266 -\beamer@ypos=\count267 -\beamer@ypos@offset=\count268 -\beamer@showpartnumber=\count269 -\beamer@currentsubsection=\count270 -\beamer@coveringdepth=\count271 -\beamer@sectionadjust=\count272 -\beamer@toclastsection=\count273 -\beamer@tocsectionnumber=\count274 - -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseoptions.sty -(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics/keyval.sty -Package: keyval 2022/05/29 v1.15 key=value parser (DPC) -\KV@toks@=\toks16 -)) -\beamer@paperwidth=\skip49 -\beamer@paperheight=\skip50 - -(/usr/local/texlive/2023/texmf-dist/tex/latex/geometry/geometry.sty -Package: geometry 2020/01/02 v5.9 Page Geometry - -(/usr/local/texlive/2023/texmf-dist/tex/generic/iftex/ifvtex.sty -Package: ifvtex 2019/10/25 v1.7 ifvtex legacy package. Use iftex instead. -) -\Gm@cnth=\count275 -\Gm@cntv=\count276 -\c@Gm@tempcnt=\count277 -\Gm@bindingoffset=\dimen155 -\Gm@wd@mp=\dimen156 -\Gm@odd@mp=\dimen157 -\Gm@even@mp=\dimen158 -\Gm@layoutwidth=\dimen159 -\Gm@layoutheight=\dimen160 -\Gm@layouthoffset=\dimen161 -\Gm@layoutvoffset=\dimen162 -\Gm@dimlist=\toks17 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/math/pgfmath.sty -(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.te -x -\pgfutil@everybye=\toks18 -\pgfutil@tempdima=\dimen163 -\pgfutil@tempdimb=\dimen164 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def -\pgfutil@abb=\box60 -) (/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/pgf.revision.tex) -Package: pgfrcs 2023-01-15 v3.1.10 (3.1.10) -)) -(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex -\pgfkeys@pathtoks=\toks19 -\pgfkeys@temptoks=\toks20 - -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfil -tered.code.tex -\pgfkeys@tmptoks=\toks21 -))) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex -\pgf@x=\dimen165 -\pgf@xa=\dimen166 -\pgf@xb=\dimen167 -\pgf@xc=\dimen168 -\pgf@y=\dimen169 -\pgf@ya=\dimen170 -\pgf@yb=\dimen171 -\pgf@yc=\dimen172 -\c@pgf@counta=\count278 -\c@pgf@countb=\count279 -\c@pgf@countc=\count280 -\c@pgf@countd=\count281 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex -\pgfmath@dimen=\dimen173 -\pgfmath@count=\count282 -\pgfmath@box=\box61 -\pgfmath@toks=\toks22 -\pgfmath@stack@operand=\toks23 -\pgfmath@stack@operation=\toks24 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code. -tex) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic -.code.tex) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigo -nometric.code.tex) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.rando -m.code.tex) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.compa -rison.code.tex) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base. -code.tex) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round -.code.tex) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc. -code.tex) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integ -erarithmetics.code.tex) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex -\c@pgfmathroundto@lastzeros=\count283 -))) (/usr/local/texlive/2023/texmf-dist/tex/latex/base/size10.clo -File: size10.clo 2022/07/02 v1.4n Standard LaTeX file (size option) -luaotfload | db : Font names database loaded from /Users/zitek/Library/texlive/2 -023/texmf-var/luatex-cache/generic/names/luaotfload-names.luc.gz) -(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty -(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics/graphicx.sty -Package: graphicx 2021/09/16 v1.2d Enhanced LaTeX Graphics (DPC,SPQR) - -(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics/graphics.sty -Package: graphics 2022/03/10 v1.4e Standard LaTeX Graphics (DPC,SPQR) - -(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics/trig.sty -Package: trig 2021/08/11 v1.11 sin cos tan (DPC) -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics-cfg/graphics.cfg -File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration -) -Package graphics Info: Driver file: luatex.def on input line 107. - -(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics-def/luatex.def -File: luatex.def 2022/09/22 v1.2d Graphics/color driver for luatex -)) -\Gin@req@height=\dimen174 -\Gin@req@width=\dimen175 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex -Package: pgfsys 2023-01-15 v3.1.10 (3.1.10) -\pgf@x=\dimen176 -\pgf@y=\dimen177 -\pgf@xa=\dimen178 -\pgf@ya=\dimen179 -\pgf@xb=\dimen180 -\pgf@yb=\dimen181 -\pgf@xc=\dimen182 -\pgf@yc=\dimen183 -\pgf@xd=\dimen184 -\pgf@yd=\dimen185 -\w@pgf@writea=\write3 -\r@pgf@reada=\read2 -\c@pgf@counta=\count284 -\c@pgf@countb=\count285 -\c@pgf@countc=\count286 -\c@pgf@countd=\count287 -\t@pgf@toka=\toks25 -\t@pgf@tokb=\toks26 -\t@pgf@tokc=\toks27 -\pgf@sys@id@count=\count288 -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg -File: pgf.cfg 2023-01-15 v3.1.10 (3.1.10) -) -Driver file for pgf: pgfsys-luatex.def - -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-luatex.d -ef -File: pgfsys-luatex.def 2023-01-15 v3.1.10 (3.1.10) - -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-p -df.def -File: pgfsys-common-pdf.def 2023-01-15 v3.1.10 (3.1.10) -))) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath. -code.tex -File: pgfsyssoftpath.code.tex 2023-01-15 v3.1.10 (3.1.10) -\pgfsyssoftpath@smallbuffer@items=\count289 -\pgfsyssoftpath@bigbuffer@items=\count290 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol. -code.tex -File: pgfsysprotocol.code.tex 2023-01-15 v3.1.10 (3.1.10) -)) (/usr/local/texlive/2023/texmf-dist/tex/latex/xcolor/xcolor.sty -Package: xcolor 2022/06/12 v2.14 LaTeX color extensions (UK) - -(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics-cfg/color.cfg -File: color.cfg 2016/01/02 v1.6 sample color configuration -) -Package xcolor Info: Driver file: luatex.def on input line 227. - -(/usr/local/texlive/2023/texmf-dist/tex/latex/graphics/mathcolor.ltx) -Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1353. -Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1357. -Package xcolor Info: Model `RGB' extended on input line 1369. -Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1371. -Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1372. -Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1373. -Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1374. -Package xcolor Info: Model `Gray' substituted by `gray' on input line 1375. -Package xcolor Info: Model `wave' substituted by `hsb' on input line 1376. -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex -Package: pgfcore 2023-01-15 v3.1.10 (3.1.10) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/math/pgfint.code.tex) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.co -de.tex -File: pgfcorepoints.code.tex 2023-01-15 v3.1.10 (3.1.10) -\pgf@picminx=\dimen186 -\pgf@picmaxx=\dimen187 -\pgf@picminy=\dimen188 -\pgf@picmaxy=\dimen189 -\pgf@pathminx=\dimen190 -\pgf@pathmaxx=\dimen191 -\pgf@pathminy=\dimen192 -\pgf@pathmaxy=\dimen193 -\pgf@xx=\dimen194 -\pgf@xy=\dimen195 -\pgf@yx=\dimen196 -\pgf@yy=\dimen197 -\pgf@zx=\dimen198 -\pgf@zy=\dimen199 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconst -ruct.code.tex -File: pgfcorepathconstruct.code.tex 2023-01-15 v3.1.10 (3.1.10) -\pgf@path@lastx=\dimen256 -\pgf@path@lasty=\dimen257 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage -.code.tex -File: pgfcorepathusage.code.tex 2023-01-15 v3.1.10 (3.1.10) -\pgf@shorten@end@additional=\dimen258 -\pgf@shorten@start@additional=\dimen259 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.co -de.tex -File: pgfcorescopes.code.tex 2023-01-15 v3.1.10 (3.1.10) -\pgfpic=\box62 -\pgf@hbox=\box63 -\pgf@layerbox@main=\box64 -\pgf@picture@serial@count=\count291 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicst -ate.code.tex -File: pgfcoregraphicstate.code.tex 2023-01-15 v3.1.10 (3.1.10) -\pgflinewidth=\dimen260 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransform -ations.code.tex -File: pgfcoretransformations.code.tex 2023-01-15 v3.1.10 (3.1.10) -\pgf@pt@x=\dimen261 -\pgf@pt@y=\dimen262 -\pgf@pt@temp=\dimen263 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.cod -e.tex -File: pgfcorequick.code.tex 2023-01-15 v3.1.10 (3.1.10) -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.c -ode.tex -File: pgfcoreobjects.code.tex 2023-01-15 v3.1.10 (3.1.10) -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathproce -ssing.code.tex -File: pgfcorepathprocessing.code.tex 2023-01-15 v3.1.10 (3.1.10) -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.co -de.tex -File: pgfcorearrows.code.tex 2023-01-15 v3.1.10 (3.1.10) -\pgfarrowsep=\dimen264 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.cod -e.tex -File: pgfcoreshade.code.tex 2023-01-15 v3.1.10 (3.1.10) -\pgf@max=\dimen265 -\pgf@sys@shading@range@num=\count292 -\pgf@shadingcount=\count293 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.cod -e.tex -File: pgfcoreimage.code.tex 2023-01-15 v3.1.10 (3.1.10) -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal. -code.tex -File: pgfcoreexternal.code.tex 2023-01-15 v3.1.10 (3.1.10) -\pgfexternal@startupbox=\box65 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.co -de.tex -File: pgfcorelayers.code.tex 2023-01-15 v3.1.10 (3.1.10) -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretranspare -ncy.code.tex -File: pgfcoretransparency.code.tex 2023-01-15 v3.1.10 (3.1.10) -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns. -code.tex -File: pgfcorepatterns.code.tex 2023-01-15 v3.1.10 (3.1.10) -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code. -tex -File: pgfcorerdf.code.tex 2023-01-15 v3.1.10 (3.1.10) -))) (/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/utilities/xxcolor.sty -Package: xxcolor 2003/10/24 ver 0.1 -\XC@nummixins=\count294 -\XC@countmixins=\count295 -) (/usr/local/texlive/2023/texmf-dist/tex/latex/base/atbegshi-ltx.sty -Package: atbegshi-ltx 2021/01/10 v1.0c Emulation of the original atbegshi -package with kernel methods -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/hyperref/hyperref.sty -Package: hyperref 2023-02-07 v7.00v Hypertext links for LaTeX - -(/usr/local/texlive/2023/texmf-dist/tex/generic/ltxcmds/ltxcmds.sty -Package: ltxcmds 2020-05-10 v1.25 LaTeX kernel commands for general use (HO) -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pdftexcmds/pdftexcmds.sty -Package: pdftexcmds 2020-06-27 v0.33 Utility functions of pdfTeX for LuaTeX (HO -) - -(/usr/local/texlive/2023/texmf-dist/tex/generic/infwarerr/infwarerr.sty -Package: infwarerr 2019/12/03 v1.5 Providing info/warning/error messages (HO) -) -Package pdftexcmds Info: \pdf@primitive is available. -Package pdftexcmds Info: \pdf@ifprimitive is available. -Package pdftexcmds Info: \pdfdraftmode found. -\pdftexcmds@toks=\toks28 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/kvsetkeys/kvsetkeys.sty -Package: kvsetkeys 2022-10-05 v1.19 Key value parser (HO) -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/kvdefinekeys/kvdefinekeys.sty -Package: kvdefinekeys 2019-12-19 v1.6 Define keys (HO) -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pdfescape/pdfescape.sty -Package: pdfescape 2019/12/09 v1.15 Implements pdfTeX's escape features (HO) -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/hycolor/hycolor.sty -Package: hycolor 2020-01-27 v1.10 Color options for hyperref/bookmark (HO) -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/letltxmacro/letltxmacro.sty -Package: letltxmacro 2019/12/03 v1.6 Let assignment for LaTeX macros (HO) -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/auxhook/auxhook.sty -Package: auxhook 2019-12-17 v1.6 Hooks for auxiliary files (HO) -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/hyperref/nameref.sty -Package: nameref 2022-05-17 v2.50 Cross-referencing by name of section - -(/usr/local/texlive/2023/texmf-dist/tex/latex/refcount/refcount.sty -Package: refcount 2019/12/15 v3.6 Data extraction from label references (HO) -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/gettitlestring/gettitlestring.s -ty -Package: gettitlestring 2019/12/15 v1.6 Cleanup title references (HO) - (/usr/local/texlive/2023/texmf-dist/tex/latex/kvoptions/kvoptions.sty -Package: kvoptions 2022-06-15 v3.15 Key value format for package options (HO) -)) -\c@section@level=\count296 -) -\@linkdim=\dimen266 -\Hy@linkcounter=\count297 -\Hy@pagecounter=\count298 - -(/usr/local/texlive/2023/texmf-dist/tex/latex/hyperref/pd1enc.def -File: pd1enc.def 2023-02-07 v7.00v Hyperref: PDFDocEncoding definition (HO) -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/intcalc/intcalc.sty -Package: intcalc 2019/12/15 v1.3 Expandable calculations with integers (HO) -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/etexcmds/etexcmds.sty -Package: etexcmds 2019/12/15 v1.7 Avoid name clashes with e-TeX commands (HO) -) -\Hy@SavedSpaceFactor=\count299 - -(/usr/local/texlive/2023/texmf-dist/tex/latex/hyperref/puenc.def -File: puenc.def 2023-02-07 v7.00v Hyperref: PDF Unicode definition (HO) -) -Package hyperref Info: Option `bookmarks' set `true' on input line 4060. -Package hyperref Info: Option `bookmarksopen' set `true' on input line 4060. -Package hyperref Info: Option `implicit' set `false' on input line 4060. -Package hyperref Info: Hyper figures OFF on input line 4177. -Package hyperref Info: Link nesting OFF on input line 4182. -Package hyperref Info: Hyper index ON on input line 4185. -Package hyperref Info: Plain pages OFF on input line 4192. -Package hyperref Info: Backreferencing OFF on input line 4197. -Package hyperref Info: Implicit mode OFF; no redefinition of LaTeX internals. -Package hyperref Info: Bookmarks ON on input line 4425. -\c@Hy@tempcnt=\count300 - -(/usr/local/texlive/2023/texmf-dist/tex/latex/url/url.sty -\Urlmuskip=\muskip16 -Package: url 2013/09/16 ver 3.4 Verb mode for urls, etc. -) -LaTeX Info: Redefining \url on input line 4763. -\XeTeXLinkMargin=\dimen267 - -(/usr/local/texlive/2023/texmf-dist/tex/generic/bitset/bitset.sty -Package: bitset 2019/12/09 v1.3 Handle bit-vector datatype (HO) - -(/usr/local/texlive/2023/texmf-dist/tex/generic/bigintcalc/bigintcalc.sty -Package: bigintcalc 2019/12/15 v1.5 Expandable calculations on big integers (HO -) -)) -\Fld@menulength=\count301 -\Field@Width=\dimen268 -\Fld@charsize=\dimen269 -Package hyperref Info: Hyper figures OFF on input line 6042. -Package hyperref Info: Link nesting OFF on input line 6047. -Package hyperref Info: Hyper index ON on input line 6050. -Package hyperref Info: backreferencing OFF on input line 6057. -Package hyperref Info: Link coloring OFF on input line 6062. -Package hyperref Info: Link coloring with OCG OFF on input line 6067. -Package hyperref Info: PDF/A mode OFF on input line 6072. -\Hy@abspage=\count302 - - -Package hyperref Message: Stopped early. - -) -Package hyperref Info: Driver (autodetected): hluatex. - (/usr/local/texlive/2023/texmf-dist/tex/latex/hyperref/hluatex.def -File: hluatex.def 2023-02-07 v7.00v Hyperref driver for luaTeX - -(/usr/local/texlive/2023/texmf-dist/tex/generic/stringenc/stringenc.sty -Package: stringenc 2019/11/29 v1.12 Convert strings between diff. encodings (HO -) -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/base/atveryend-ltx.sty -Package: atveryend-ltx 2020/08/19 v1.0a Emulation of the original atveryend pac -kage -with kernel methods -) -\Fld@listcount=\count303 -\c@bookmark@seq@number=\count304 - -(/usr/local/texlive/2023/texmf-dist/tex/latex/rerunfilecheck/rerunfilecheck.sty -Package: rerunfilecheck 2022-07-10 v1.10 Rerun checks for auxiliary files (HO) - -(/usr/local/texlive/2023/texmf-dist/tex/generic/uniquecounter/uniquecounter.sty -Package: uniquecounter 2019/12/15 v1.4 Provide unlimited unique counter (HO) -) -Package uniquecounter Info: New unique counter `rerunfilecheck' on input line 2 -85. -)) (/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaserequires.sty -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasecompatibility.st -y) (/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasefont.sty -(/usr/local/texlive/2023/texmf-dist/tex/latex/amsfonts/amssymb.sty -Package: amssymb 2013/01/14 v3.01 AMS font symbols - -(/usr/local/texlive/2023/texmf-dist/tex/latex/amsfonts/amsfonts.sty -Package: amsfonts 2013/01/14 v3.01 Basic AMSFonts support -\@emptytoks=\toks29 -\symAMSa=\mathgroup4 -\symAMSb=\mathgroup5 -LaTeX Font Info: Redeclaring math symbol \hbar on input line 98. -LaTeX Font Info: Overwriting math alphabet `\mathfrak' in version `bold' -(Font) U/euf/m/n --> U/euf/b/n on input line 106. -)) -(/usr/local/texlive/2023/texmf-dist/tex/latex/sansmathaccent/sansmathaccent.sty -Package: sansmathaccent 2020/01/31 -(/usr/local/texlive/2023/texmf-dist/tex/latex/koma-script/scrlfile.sty -Package: scrlfile 2022/10/12 v3.38 KOMA-Script package (file load hooks) - -(/usr/local/texlive/2023/texmf-dist/tex/latex/koma-script/scrlfile-hook.sty -Package: scrlfile-hook 2022/10/12 v3.38 KOMA-Script package (using LaTeX hooks) - - -(/usr/local/texlive/2023/texmf-dist/tex/latex/koma-script/scrlogo.sty -Package: scrlogo 2022/10/12 v3.38 KOMA-Script package (logo) -))))) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasetranslator.sty -(/usr/local/texlive/2023/texmf-dist/tex/latex/translator/translator.sty -Package: translator 2021-05-31 v1.12d Easy translation of strings in LaTeX -)) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasemisc.sty) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasetwoscreens.sty) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseoverlay.sty -\beamer@argscount=\count305 -\beamer@lastskipcover=\skip51 -\beamer@trivlistdepth=\count306 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasetitle.sty) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasesection.sty -\c@lecture=\count307 -\c@part=\count308 -\c@section=\count309 -\c@subsection=\count310 -\c@subsubsection=\count311 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseframe.sty -\beamer@framebox=\box66 -\beamer@frametitlebox=\box67 -\beamer@zoombox=\box68 -\beamer@zoomcount=\count312 -\beamer@zoomframecount=\count313 -\beamer@frametextheight=\dimen270 -\c@subsectionslide=\count314 -\beamer@frametopskip=\skip52 -\beamer@framebottomskip=\skip53 -\beamer@frametopskipautobreak=\skip54 -\beamer@framebottomskipautobreak=\skip55 -\beamer@envbody=\toks30 -\framewidth=\dimen271 -\c@framenumber=\count315 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseverbatim.sty -\beamer@verbatimfileout=\write4 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseframesize.sty -\beamer@splitbox=\box69 -\beamer@autobreakcount=\count316 -\beamer@autobreaklastheight=\dimen272 -\beamer@frametitletoks=\toks31 -\beamer@framesubtitletoks=\toks32 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseframecomponents. -sty -\beamer@footins=\box70 -) (/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasecolor.sty) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasenotes.sty -\beamer@frameboxcopy=\box71 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasetoc.sty) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasetemplates.sty -\beamer@sbttoks=\toks33 - -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseauxtemplates.sty -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaseboxes.sty -\bmb@box=\box72 -\bmb@colorbox=\box73 -\bmb@boxwidth=\dimen273 -\bmb@boxheight=\dimen274 -\bmb@prevheight=\dimen275 -\bmb@temp=\dimen276 -\bmb@dima=\dimen277 -\bmb@dimb=\dimen278 -\bmb@prevheight=\dimen279 -) -\beamer@blockheadheight=\dimen280 -)) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbaselocalstructure.s -ty (/usr/local/texlive/2023/texmf-dist/tex/latex/tools/enumerate.sty -Package: enumerate 2015/07/23 v3.00 enumerate extensions (DPC) -\@enLab=\toks34 -) -\beamer@bibiconwidth=\skip56 -\c@figure=\count317 -\c@table=\count318 -\abovecaptionskip=\skip57 -\belowcaptionskip=\skip58 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasenavigation.sty -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasenavigationsymbol -s.tex) -\beamer@section@min@dim=\dimen281 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasetheorems.sty -(/usr/local/texlive/2023/texmf-dist/tex/latex/amsmath/amsmath.sty -Package: amsmath 2022/04/08 v2.17n AMS math features -\@mathmargin=\skip59 - -For additional information on amsmath, use the `?' option. -(/usr/local/texlive/2023/texmf-dist/tex/latex/amsmath/amstext.sty -Package: amstext 2021/08/26 v2.01 AMS text - -(/usr/local/texlive/2023/texmf-dist/tex/latex/amsmath/amsgen.sty -File: amsgen.sty 1999/11/30 v2.0 generic functions -\@emptytoks=\toks35 -\ex@=\dimen282 -)) -(/usr/local/texlive/2023/texmf-dist/tex/latex/amsmath/amsbsy.sty -Package: amsbsy 1999/11/29 v1.2d Bold Symbols -\pmbraise@=\dimen283 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/amsmath/amsopn.sty -Package: amsopn 2022/04/08 v2.04 operator names -) -\inf@bad=\count319 -LaTeX Info: Redefining \frac on input line 236. -\uproot@=\count320 -\leftroot@=\count321 -LaTeX Info: Redefining \overline on input line 399. -LaTeX Info: Redefining \colon on input line 410. -\classnum@=\count322 -\DOTSCASE@=\count323 -LaTeX Info: Redefining \ldots on input line 496. -LaTeX Info: Redefining \dots on input line 499. -LaTeX Info: Redefining \cdots on input line 620. -\Mathstrutbox@=\box74 -\strutbox@=\box75 -LaTeX Info: Redefining \big on input line 722. -LaTeX Info: Redefining \Big on input line 723. -LaTeX Info: Redefining \bigg on input line 724. -LaTeX Info: Redefining \Bigg on input line 725. -\big@size=\dimen284 -LaTeX Font Info: Redeclaring font encoding OML on input line 743. -LaTeX Font Info: Redeclaring font encoding OMS on input line 744. -\macc@depth=\count324 -LaTeX Info: Redefining \bmod on input line 905. -LaTeX Info: Redefining \pmod on input line 910. -LaTeX Info: Redefining \smash on input line 940. -LaTeX Info: Redefining \relbar on input line 970. -LaTeX Info: Redefining \Relbar on input line 971. -\c@MaxMatrixCols=\count325 -\dotsspace@=\muskip17 -\c@parentequation=\count326 -\dspbrk@lvl=\count327 -\tag@help=\toks36 -\row@=\count328 -\column@=\count329 -\maxfields@=\count330 -\andhelp@=\toks37 -\eqnshift@=\dimen285 -\alignsep@=\dimen286 -\tagshift@=\dimen287 -\tagwidth@=\dimen288 -\totwidth@=\dimen289 -\lineht@=\dimen290 -\@envbody=\toks38 -\multlinegap=\skip60 -\multlinetaggap=\skip61 -\mathdisplay@stack=\toks39 -LaTeX Info: Redefining \[ on input line 2953. -LaTeX Info: Redefining \] on input line 2954. -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/amscls/amsthm.sty -Package: amsthm 2020/05/29 v2.20.6 -\thm@style=\toks40 -\thm@bodyfont=\toks41 -\thm@headfont=\toks42 -\thm@notefont=\toks43 -\thm@headpunct=\toks44 -\thm@preskip=\skip62 -\thm@postskip=\skip63 -\thm@headsep=\skip64 -\dth@everypar=\toks45 -) -\c@theorem=\count331 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerbasethemes.sty)) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerthemedefault.sty -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerfontthemedefault.sty -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamercolorthemedefault.st -y) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerinnerthemedefault.st -y -\beamer@dima=\dimen291 -\beamer@dimb=\dimen292 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamer/beamerouterthemedefault.st -y))) (/usr/local/texlive/2023/texmf-dist/tex/latex/listings/listings.sty -\lst@mode=\count332 -\lst@gtempboxa=\box76 -\lst@token=\toks46 -\lst@length=\count333 -\lst@currlwidth=\dimen293 -\lst@column=\count334 -\lst@pos=\count335 -\lst@lostspace=\dimen294 -\lst@width=\dimen295 -\lst@newlines=\count336 -\lst@lineno=\count337 -\lst@maxwidth=\dimen296 - -(/usr/local/texlive/2023/texmf-dist/tex/latex/listings/lstmisc.sty -File: lstmisc.sty 2023/02/27 1.9 (Carsten Heinz) -\c@lstnumber=\count338 -\lst@skipnumbers=\count339 -\lst@framebox=\box77 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/listings/listings.cfg -File: listings.cfg 2023/02/27 1.9 listings configuration -)) -Package: listings 2023/02/27 1.9 (Carsten Heinz) - -(/usr/local/texlive/2023/texmf-dist/tex/latex/booktabs/booktabs.sty -Package: booktabs 2020/01/12 v1.61803398 Publication quality tables -\heavyrulewidth=\dimen297 -\lightrulewidth=\dimen298 -\cmidrulewidth=\dimen299 -\belowrulesep=\dimen300 -\belowbottomsep=\dimen301 -\aboverulesep=\dimen302 -\abovetopsep=\dimen303 -\cmidrulesep=\dimen304 -\cmidrulekern=\dimen305 -\defaultaddspace=\dimen306 -\@cmidla=\count340 -\@cmidlb=\count341 -\@aboverulesep=\dimen307 -\@belowrulesep=\dimen308 -\@thisruleclass=\count342 -\@lastruleclass=\count343 -\@thisrulewidth=\dimen309 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/tools/array.sty -Package: array 2022/09/04 v2.5g Tabular extension package (FMi) -\col@sep=\dimen310 -\ar@mcellbox=\box78 -\extrarowheight=\dimen311 -\NC@list=\toks47 -\extratabsurround=\skip65 -\backup@length=\skip66 -\ar@cellbox=\box79 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamertheme-metropolis/beamerthem -emetropolis.sty -Package: beamerthememetropolis 2017/01/23 v1.2 Metropolis Beamer theme - -(/usr/local/texlive/2023/texmf-dist/tex/latex/pgfopts/pgfopts.sty -Package: pgfopts 2014/07/10 v2.1a LaTeX package options with pgfkeys -\pgfopts@list@add@a@toks=\toks48 -\pgfopts@list@add@b@toks=\toks49 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamertheme-metropolis/beamerinne -rthememetropolis.sty -Package: beamerinnerthememetropolis 2017/01/23 Metropolis inner theme - -(/usr/local/texlive/2023/texmf-dist/tex/latex/tools/calc.sty -Package: calc 2017/05/25 v4.3 Infix arithmetic (KKT,FJ) -\calc@Acount=\count344 -\calc@Bcount=\count345 -\calc@Adimen=\dimen312 -\calc@Bdimen=\dimen313 -\calc@Askip=\skip67 -\calc@Bskip=\skip68 -LaTeX Info: Redefining \setlength on input line 80. -LaTeX Info: Redefining \addtolength on input line 81. -\calc@Ccount=\count346 -\calc@Cskip=\skip69 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty -(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty -Package: pgf 2023-01-15 v3.1.10 (3.1.10) - -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.cod -e.tex -File: pgfmoduleshapes.code.tex 2023-01-15 v3.1.10 (3.1.10) -\pgfnodeparttextbox=\box80 -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code. -tex -File: pgfmoduleplot.code.tex 2023-01-15 v3.1.10 (3.1.10) -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version --0-65.sty -Package: pgfcomp-version-0-65 2023-01-15 v3.1.10 (3.1.10) -\pgf@nodesepstart=\dimen314 -\pgf@nodesepend=\dimen315 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version --1-18.sty -Package: pgfcomp-version-1-18 2023-01-15 v3.1.10 (3.1.10) -)) -(/usr/local/texlive/2023/texmf-dist/tex/latex/pgf/utilities/pgffor.sty -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex -Package: pgffor 2023-01-15 v3.1.10 (3.1.10) -\pgffor@iter=\dimen316 -\pgffor@skip=\dimen317 -\pgffor@stack=\toks50 -\pgffor@toks=\toks51 -)) -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.cod -e.tex -Package: tikz 2023-01-15 v3.1.10 (3.1.10) - -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothan -dlers.code.tex -File: pgflibraryplothandlers.code.tex 2023-01-15 v3.1.10 (3.1.10) -\pgf@plot@mark@count=\count347 -\pgfplotmarksize=\dimen318 -) -\tikz@lastx=\dimen319 -\tikz@lasty=\dimen320 -\tikz@lastxsaved=\dimen321 -\tikz@lastysaved=\dimen322 -\tikz@lastmovetox=\dimen323 -\tikz@lastmovetoy=\dimen324 -\tikzleveldistance=\dimen325 -\tikzsiblingdistance=\dimen326 -\tikz@figbox=\box81 -\tikz@figbox@bg=\box82 -\tikz@tempbox=\box83 -\tikz@tempbox@bg=\box84 -\tikztreelevel=\count348 -\tikznumberofchildren=\count349 -\tikznumberofcurrentchild=\count350 -\tikz@fig@count=\count351 - -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.cod -e.tex -File: pgfmodulematrix.code.tex 2023-01-15 v3.1.10 (3.1.10) -\pgfmatrixcurrentrow=\count352 -\pgfmatrixcurrentcolumn=\count353 -\pgf@matrix@numberofcolumns=\count354 -) -\tikz@expandcount=\count355 - -(/usr/local/texlive/2023/texmf-dist/tex/generic/pgf/frontendlayer/tikz/librarie -s/tikzlibrarytopaths.code.tex -File: tikzlibrarytopaths.code.tex 2023-01-15 v3.1.10 (3.1.10) -))) -\metropolis@titleseparator@linewidth=\skip70 -\metropolis@progressonsectionpage=\skip71 -\metropolis@progressonsectionpage@linewidth=\skip72 -\metropolis@blocksep=\skip73 -\metropolis@blockadjust=\skip74 -\metropolis@parskip=\skip75 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamertheme-metropolis/beameroute -rthememetropolis.sty -Package: beamerouterthememetropolis 2017/01/23 Metropolis outer theme -\metropolis@frametitle@padding=\skip76 -\metropolis@progressinheadfoot=\skip77 -\metropolis@progressinheadfoot@linewidth=\skip78 -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamertheme-metropolis/beamercolo -rthememetropolis.sty -Package: beamercolorthememetropolis 2017/01/23 Metropolis color theme -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/beamertheme-metropolis/beamerfont -thememetropolis.sty -Package: beamerfontthememetropolis 2017/01/23 Metropolis font theme - -(/usr/local/texlive/2023/texmf-dist/tex/generic/iftex/ifxetex.sty -Package: ifxetex 2019/10/25 v0.7 ifxetex legacy package. Use iftex instead. -) -(/usr/local/texlive/2023/texmf-dist/tex/generic/iftex/ifluatex.sty -Package: ifluatex 2019/10/25 v1.5 ifluatex legacy package. Use iftex instead. -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/fontspec/fontspec.sty -(/usr/local/texlive/2023/texmf-dist/tex/latex/l3packages/xparse/xparse.sty -(/usr/local/texlive/2023/texmf-dist/tex/latex/l3kernel/expl3.sty -Package: expl3 2023-02-22 L3 programming layer (loader) - -(/usr/local/texlive/2023/texmf-dist/tex/latex/l3backend/l3backend-luatex.def -File: l3backend-luatex.def 2023-01-16 L3 backend support: PDF output (LuaTeX) -\l__color_backend_stack_int=\count356 -\l__pdf_internal_box=\box85 -)) -Package: xparse 2023-02-02 L3 Experimental document command parser -) -Package: fontspec 2022/01/15 v2.8a Font selection for XeLaTeX and LuaLaTeX -Lua module: fontspec 2022/01/15 2.8a Font selection for XeLaTeX and LuaLaTeX (/ -usr/local/texlive/2023/texmf-dist/tex/latex/fontspec/fontspec-luatex.sty -Package: fontspec-luatex 2022/01/15 v2.8a Font selection for XeLaTeX and LuaLaT -eX -\l__fontspec_script_int=\count357 -\l__fontspec_language_int=\count358 -\l__fontspec_strnum_int=\count359 -\l__fontspec_tmp_int=\count360 -\l__fontspec_tmpa_int=\count361 -\l__fontspec_tmpb_int=\count362 -\l__fontspec_tmpc_int=\count363 -\l__fontspec_em_int=\count364 -\l__fontspec_emdef_int=\count365 -\l__fontspec_strong_int=\count366 -\l__fontspec_strongdef_int=\count367 -\l__fontspec_tmpa_dim=\dimen327 -\l__fontspec_tmpb_dim=\dimen328 -\l__fontspec_tmpc_dim=\dimen329 - -(/usr/local/texlive/2023/texmf-dist/tex/latex/base/fontenc.sty -Package: fontenc 2021/04/29 v2.0v Standard LaTeX package -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/fontspec/fontspec.cfg))) -\c@fontsnotfound=\count368 -luaotfload | cache : Lookup cache loaded from /Users/zitek/Library/texlive/2023/ -texmf-var/luatex-cache/generic/names/luaotfload-lookup-cache.luc. - -Package fontspec Info: Font family 'FiraSansLight(0)' created for font 'Fira -(fontspec) Sans Light' with options -(fontspec) [Ligatures=TeX,ItalicFont={Fira Sans Light -(fontspec) Italic},BoldFont={Fira Sans},BoldItalicFont={Fira Sans -(fontspec) Italic}]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->"FiraSansLight:mode=node;script=latn;language=dflt;+t -lig;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->"FiraSansLight:mode=node;script=latn;language=dflt;+t -lig;+smcp;" -(fontspec) - 'bold' (b/n) with NFSS spec.: -(fontspec) <->"FiraSans:mode=node;script=latn;language=dflt;+tlig;" - -(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: -(fontspec) <->"FiraSans:mode=node;script=latn;language=dflt;+tlig;+ -smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->"FiraSansLightItalic:mode=node;script=latn;language=d -flt;+tlig;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->"FiraSansLightItalic:mode=node;script=latn;language=d -flt;+tlig;+smcp;" -(fontspec) - 'bold italic' (b/it) with NFSS spec.: -(fontspec) <->"FiraSansItalic:mode=node;script=latn;language=dflt;+ -tlig;" -(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: -(fontspec) <->"FiraSansItalic:mode=node;script=latn;language=dflt;+ -tlig;+smcp;" - - -Package fontspec Info: Could not resolve font "FiraMonoMedium/I" (it probably -(fontspec) doesn't exist). - -luaotfload | aux : font no 37 (nil) does not define feature smcp for script latn - with language dflt -luaotfload | aux : font no 38 (nil) does not define feature smcp for script latn - with language dflt -luaotfload | aux : font no 39 (nil) does not define feature smcp for script latn - with language dflt -luaotfload | aux : font no 40 (nil) does not define feature smcp for script latn - with language dflt - -Package fontspec Info: Font family 'FiraMono(0)' created for font 'Fira Mono' -(fontspec) with options -(fontspec) [WordSpace={1,0,0},HyphenChar=None,PunctuationSpace=Word -Space,BoldFont={Fira -(fontspec) Mono Medium}]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->"FiraMono:mode=node;script=latn;language=dflt;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) and font adjustment code: -(fontspec) \fontdimen 2\font =1\fontdimen 2\font \fontdimen 3\font -(fontspec) =0\fontdimen 3\font \fontdimen 4\font =0\fontdimen -(fontspec) 4\font \fontdimen 7\font =0\fontdimen 2\font -(fontspec) \tex_hyphenchar:D \font =-1\scan_stop: -(fontspec) - 'bold' (b/n) with NFSS spec.: -(fontspec) <->"FiraMonoMedium:mode=node;script=latn;language=dflt;" - -(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: -(fontspec) and font adjustment code: -(fontspec) \fontdimen 2\font =1\fontdimen 2\font \fontdimen 3\font -(fontspec) =0\fontdimen 3\font \fontdimen 4\font =0\fontdimen -(fontspec) 4\font \fontdimen 7\font =0\fontdimen 2\font -(fontspec) \tex_hyphenchar:D \font =-1\scan_stop: -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->"FiraMono/I:mode=node;script=latn;language=dflt;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) and font adjustment code: -(fontspec) \fontdimen 2\font =1\fontdimen 2\font \fontdimen 3\font -(fontspec) =0\fontdimen 3\font \fontdimen 4\font =0\fontdimen -(fontspec) 4\font \fontdimen 7\font =0\fontdimen 2\font -(fontspec) \tex_hyphenchar:D \font =-1\scan_stop: -(fontspec) - 'bold italic' (b/it) with NFSS spec.: -(fontspec) <->"FiraMono/BI:mode=node;script=latn;language=dflt;" -(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: -(fontspec) and font adjustment code: -(fontspec) \fontdimen 2\font =1\fontdimen 2\font \fontdimen 3\font -(fontspec) =0\fontdimen 3\font \fontdimen 4\font =0\fontdimen -(fontspec) 4\font \fontdimen 7\font =0\fontdimen 2\font -(fontspec) \tex_hyphenchar:D \font =-1\scan_stop: - -)) -(/usr/local/texlive/2023/texmf-dist/tex/latex/fira/FiraSans.sty -Package: FiraSans 2022/09/17 (Bob Tennent and autoinst) Style file for Fira San -s fonts. - -(/usr/local/texlive/2023/texmf-dist/tex/latex/xkeyval/xkeyval.sty -Package: xkeyval 2022/06/16 v2.9 package option processing (HA) - -(/usr/local/texlive/2023/texmf-dist/tex/generic/xkeyval/xkeyval.tex -(/usr/local/texlive/2023/texmf-dist/tex/generic/xkeyval/xkvutils.tex -\XKV@toks=\toks52 -\XKV@tempa@toks=\toks53 -) -\XKV@depth=\count369 -File: xkeyval.tex 2014/12/03 v2.7a key=value parser (HA) -)) -(/usr/local/texlive/2023/texmf-dist/tex/latex/base/textcomp.sty -Package: textcomp 2020/02/02 v2.0n Standard LaTeX package -) - -Package fontspec Info: Font family 'FiraSans(0)' created for font 'FiraSans' -(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = -(fontspec) .otf,Ligatures=TeX,Numbers = -(fontspec) {Proportional,OldStyle},UprightFont = -(fontspec) *-Regular,ItalicFont = *-Italic,BoldFont = -(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn -;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn -;language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold' (b/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; -language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; -language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold italic' (b/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;+smcp;" - -LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be -(Font) scaled to size 8.50006pt on input line 138. - -Package fontspec Info: Font family 'FiraSans(1)' created for font 'FiraSans' -(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = -(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = -(fontspec) *-Regular,ItalicFont = *-Italic,BoldFont = -(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn -;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn -;language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold' (b/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; -language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; -language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold italic' (b/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;+smcp;" - - -Package fontspec Info: Font family 'FiraSans(2)' created for font 'FiraSans' -(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = -(fontspec) .otf,Numbers = {Proportional,Lining},UprightFont = -(fontspec) *-Regular,ItalicFont = *-Italic,BoldFont = -(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn -;language=dflt;+tlig;+pnum;+lnum;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn -;language=dflt;+tlig;+pnum;+lnum;+smcp;" -(fontspec) - 'bold' (b/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+lnum;" -(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+lnum;+smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; -language=dflt;+tlig;+pnum;+lnum;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; -language=dflt;+tlig;+pnum;+lnum;+smcp;" -(fontspec) - 'bold italic' (b/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+lnum;" -(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+lnum;+smcp;" - - -Package fontspec Info: Font family 'FiraSans(3)' created for font 'FiraSans' -(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = -(fontspec) .otf,Numbers = {Monospaced,OldStyle},UprightFont = -(fontspec) *-Regular,ItalicFont = *-Italic,BoldFont = -(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn -;language=dflt;+tlig;+tnum;+onum;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn -;language=dflt;+tlig;+tnum;+onum;+smcp;" -(fontspec) - 'bold' (b/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+tnum;+onum;" -(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+tnum;+onum;+smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; -language=dflt;+tlig;+tnum;+onum;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; -language=dflt;+tlig;+tnum;+onum;+smcp;" -(fontspec) - 'bold italic' (b/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+tnum;+onum;" -(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+tnum;+onum;+smcp;" - - -Package fontspec Info: Font family 'FiraSans(4)' created for font 'FiraSans' -(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = -(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = -(fontspec) *-Thin,ItalicFont = *-ThinItalic,BoldFont = -(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Thin.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Thin.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold' (b/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-ThinItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-ThinItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold italic' (b/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;+smcp;" - - -Package fontspec Info: Font family 'FiraSans(5)' created for font 'FiraSans' -(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = -(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = -(fontspec) *-Light,ItalicFont = *-LightItalic,BoldFont = -(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Light.otf]:mode=node;script=latn;l -anguage=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Light.otf]:mode=node;script=latn;l -anguage=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold' (b/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-LightItalic.otf]:mode=node;script= -latn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-LightItalic.otf]:mode=node;script= -latn;language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold italic' (b/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;+smcp;" - - -Package fontspec Info: Font family 'FiraSans(6)' created for font 'FiraSans' -(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = -(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = -(fontspec) *-ExtraLight,ItalicFont = *-ExtraLightItalic,BoldFont = -(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-ExtraLight.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-ExtraLight.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold' (b/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-ExtraLightItalic.otf]:mode=node;sc -ript=latn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-ExtraLightItalic.otf]:mode=node;sc -ript=latn;language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold italic' (b/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;+smcp;" - - -Package fontspec Info: Font family 'FiraSans(7)' created for font 'FiraSans' -(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = -(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = -(fontspec) *-UltraLight,ItalicFont = *-UltraLightItalic,BoldFont = -(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-UltraLight.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-UltraLight.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold' (b/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-UltraLightItalic.otf]:mode=node;sc -ript=latn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-UltraLightItalic.otf]:mode=node;sc -ript=latn;language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold italic' (b/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;+smcp;" - - -Package fontspec Info: Font family 'FiraSans(8)' created for font 'FiraSans' -(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = -(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = -(fontspec) *-Medium,ItalicFont = *-MediumItalic,BoldFont = -(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Medium.otf]:mode=node;script=latn; -language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Medium.otf]:mode=node;script=latn; -language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold' (b/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-MediumItalic.otf]:mode=node;script -=latn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-MediumItalic.otf]:mode=node;script -=latn;language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold italic' (b/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;+smcp;" - - -Package fontspec Info: Font family 'FiraSans(9)' created for font 'FiraSans' -(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = -(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = -(fontspec) *-Book,ItalicFont = *-BookItalic,BoldFont = -(fontspec) *-Bold,BoldItalicFont = *-BoldItalic]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Book.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Book.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold' (b/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BookItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BookItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'bold italic' (b/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+pnum;+onum;+smcp;" - - -Package fontspec Info: Font family 'FiraSans(10)' created for font 'FiraSans' -(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = -(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = -(fontspec) *-SemiBold,ItalicFont = *-SemiBoldItalic]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-SemiBold.otf]:mode=node;script=lat -n;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-SemiBold.otf]:mode=node;script=lat -n;language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-SemiBoldItalic.otf]:mode=node;scri -pt=latn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-SemiBoldItalic.otf]:mode=node;scri -pt=latn;language=dflt;+tlig;+pnum;+onum;+smcp;" - - -Package fontspec Info: Font family 'FiraSans(11)' created for font 'FiraSans' -(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = -(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = -(fontspec) *-ExtraBold,ItalicFont = *-ExtraBoldItalic]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-ExtraBold.otf]:mode=node;script=la -tn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-ExtraBold.otf]:mode=node;script=la -tn;language=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-ExtraBoldItalic.otf]:mode=node;scr -ipt=latn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-ExtraBoldItalic.otf]:mode=node;scr -ipt=latn;language=dflt;+tlig;+pnum;+onum;+smcp;" - - -Package fontspec Info: Font family 'FiraSans(12)' created for font 'FiraSans' -(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = -(fontspec) .otf,Numbers = {Proportional,OldStyle},UprightFont = -(fontspec) *-Heavy,ItalicFont = *-HeavyItalic]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Heavy.otf]:mode=node;script=latn;l -anguage=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Heavy.otf]:mode=node;script=latn;l -anguage=dflt;+tlig;+pnum;+onum;+smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-HeavyItalic.otf]:mode=node;script= -latn;language=dflt;+tlig;+pnum;+onum;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-HeavyItalic.otf]:mode=node;script= -latn;language=dflt;+tlig;+pnum;+onum;+smcp;" - -) (./iors.aux) -\openout1 = iors.aux - -LaTeX Font Info: Checking defaults for OML/cmm/m/it on input line 37. -LaTeX Font Info: ... okay on input line 37. -LaTeX Font Info: Checking defaults for OMS/cmsy/m/n on input line 37. -LaTeX Font Info: ... okay on input line 37. -LaTeX Font Info: Checking defaults for OT1/cmr/m/n on input line 37. -LaTeX Font Info: ... okay on input line 37. -LaTeX Font Info: Checking defaults for T1/cmr/m/n on input line 37. -LaTeX Font Info: ... okay on input line 37. -LaTeX Font Info: Checking defaults for TS1/cmr/m/n on input line 37. -LaTeX Font Info: Trying to load font information for TS1+cmr on input line 3 -7. - -(/usr/local/texlive/2023/texmf-dist/tex/latex/base/ts1cmr.fd -File: ts1cmr.fd 2022/07/10 v2.5l Standard LaTeX font definitions -) -LaTeX Font Info: ... okay on input line 37. -LaTeX Font Info: Checking defaults for TU/lmr/m/n on input line 37. -LaTeX Font Info: ... okay on input line 37. -LaTeX Font Info: Checking defaults for OMX/cmex/m/n on input line 37. -LaTeX Font Info: ... okay on input line 37. -LaTeX Font Info: Checking defaults for U/cmr/m/n on input line 37. -LaTeX Font Info: ... okay on input line 37. -LaTeX Font Info: Checking defaults for PD1/pdf/m/n on input line 37. -LaTeX Font Info: ... okay on input line 37. -LaTeX Font Info: Checking defaults for PU/pdf/m/n on input line 37. -LaTeX Font Info: ... okay on input line 37. - -*geometry* driver: auto-detecting -*geometry* detected driver: luatex -*geometry* verbose mode - [ preamble ] result: -* driver: luatex -* paper: custom -* layout: -* layoutoffset:(h,v)=(0.0pt,0.0pt) -* modes: includehead includefoot -* h-part:(L,W,R)=(28.45274pt, 398.3386pt, 28.45274pt) -* v-part:(T,H,B)=(0.0pt, 256.0748pt, 0.0pt) -* \paperwidth=455.24408pt -* \paperheight=256.0748pt -* \textwidth=398.3386pt -* \textheight=227.62207pt -* \oddsidemargin=-43.81725pt -* \evensidemargin=-43.81725pt -* \topmargin=-72.26999pt -* \headheight=14.22636pt -* \headsep=0.0pt -* \topskip=10.0pt -* \footskip=14.22636pt -* \marginparwidth=3.0pt -* \marginparsep=11.0pt -* \columnsep=10.0pt -* \skip\footins=9.0pt plus 4.0pt minus 2.0pt -* \hoffset=0.0pt -* \voffset=0.0pt -* \mag=1000 -* \@twocolumnfalse -* \@twosidefalse -* \@mparswitchfalse -* \@reversemarginfalse -* (1in=72.27pt=25.4mm, 1cm=28.453pt) - -(/usr/local/texlive/2023/texmf-dist/tex/context/base/mkii/supp-pdf.mkii -[Loading MPS to PDF converter (version 2006.09.02).] -\scratchcounter=\count370 -\scratchdimen=\dimen330 -\scratchbox=\box86 -\nofMPsegments=\count371 -\nofMParguments=\count372 -\everyMPshowfont=\toks54 -\MPscratchCnt=\count373 -\MPscratchDim=\dimen331 -\MPnumerator=\count374 -\makeMPintoPDFobject=\count375 -\everyMPtoPDFconversion=\toks55 -) (/usr/local/texlive/2023/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty -Package: epstopdf-base 2020-01-24 v2.11 Base part for package epstopdf -Package epstopdf-base Info: Redefining graphics rule for `.eps' on input line 4 -85. - -(/usr/local/texlive/2023/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg -File: epstopdf-sys.cfg 2010/07/13 v1.3 Configuration of (r)epstopdf for TeX Liv -e -)) -Package hyperref Info: Link coloring OFF on input line 37. - -(./iors.out) (./iors.out) -\@outlinefile=\write5 - -\openout5 = iors.out -LaTeX Font Info: Overwriting symbol font `operators' in version `normal' -(Font) OT1/cmr/m/n --> OT1/cmss/m/n on input line 37. -LaTeX Font Info: Overwriting symbol font `operators' in version `bold' -(Font) OT1/cmr/bx/n --> OT1/cmss/b/n on input line 37. -\symnumbers=\mathgroup6 -\sympureletters=\mathgroup7 -LaTeX Font Info: Overwriting math alphabet `\mathrm' in version `normal' -(Font) OT1/cmss/m/n --> TU/lmr/m/n on input line 37. -LaTeX Font Info: Redeclaring math alphabet \mathbf on input line 37. -LaTeX Font Info: Overwriting math alphabet `\mathbf' in version `normal' -(Font) OT1/cmr/bx/n --> TU/FiraSans(0)/b/n on input line 37. -LaTeX Font Info: Overwriting math alphabet `\mathbf' in version `bold' -(Font) OT1/cmr/bx/n --> TU/FiraSans(0)/b/n on input line 37. -LaTeX Font Info: Redeclaring math alphabet \mathsf on input line 37. -LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `normal' -(Font) OT1/cmss/m/n --> TU/FiraSans(0)/m/n on input line 37. -LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `bold' -(Font) OT1/cmss/bx/n --> TU/FiraSans(0)/m/n on input line 37. -LaTeX Font Info: Redeclaring math alphabet \mathit on input line 37. -LaTeX Font Info: Overwriting math alphabet `\mathit' in version `normal' -(Font) OT1/cmr/m/it --> TU/FiraSans(0)/m/it on input line 37. -LaTeX Font Info: Overwriting math alphabet `\mathit' in version `bold' -(Font) OT1/cmr/bx/it --> TU/FiraSans(0)/m/it on input line 37. - -LaTeX Font Info: Redeclaring math alphabet \mathtt on input line 37. -LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `normal' -(Font) OT1/cmtt/m/n --> TU/FiraMono(0)/m/n on input line 37. -LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `bold' -(Font) OT1/cmtt/m/n --> TU/FiraMono(0)/m/n on input line 37. -LaTeX Font Info: Overwriting symbol font `numbers' in version `bold' -(Font) TU/FiraSans(0)/m/n --> TU/FiraSans(0)/b/n on input line - 37. -LaTeX Font Info: Overwriting symbol font `pureletters' in version `bold' -(Font) TU/FiraSans(0)/m/it --> TU/FiraSans(0)/b/it on input li -ne 37. -LaTeX Font Info: Overwriting math alphabet `\mathrm' in version `bold' -(Font) OT1/cmss/b/n --> TU/lmr/b/n on input line 37. -LaTeX Font Info: Overwriting math alphabet `\mathbf' in version `bold' -(Font) TU/FiraSans(0)/b/n --> TU/FiraSans(0)/b/n on input line - 37. -LaTeX Font Info: Overwriting math alphabet `\mathsf' in version `bold' -(Font) TU/FiraSans(0)/m/n --> TU/FiraSans(0)/b/n on input line - 37. -LaTeX Font Info: Overwriting math alphabet `\mathit' in version `bold' -(Font) TU/FiraSans(0)/m/it --> TU/FiraSans(0)/b/it on input li -ne 37. -LaTeX Font Info: Overwriting math alphabet `\mathtt' in version `bold' -(Font) TU/FiraMono(0)/m/n --> TU/FiraMono(0)/b/n on input line - 37. - -(/usr/local/texlive/2023/texmf-dist/tex/latex/translator/translator-basic-dicti -onary-English.dict -Dictionary: translator-basic-dictionary, Language: English -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/translator/translator-bibliograph -y-dictionary-English.dict -Dictionary: translator-bibliography-dictionary, Language: English -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/translator/translator-environment --dictionary-English.dict -Dictionary: translator-environment-dictionary, Language: English -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/translator/translator-months-dict -ionary-English.dict -Dictionary: translator-months-dictionary, Language: English -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/translator/translator-numbers-dic -tionary-English.dict -Dictionary: translator-numbers-dictionary, Language: English -) -(/usr/local/texlive/2023/texmf-dist/tex/latex/translator/translator-theorem-dic -tionary-English.dict -Dictionary: translator-theorem-dictionary, Language: English -) -\c@lstlisting=\count376 - (./iors.nav) -LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be -(Font) scaled to size 4.25003pt on input line 37. -LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be -(Font) scaled to size 5.95004pt on input line 37. -LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be -(Font) scaled to size 12.24008pt on input line 39. -LaTeX Font Info: Font shape `TU/FiraSans(0)/b/n' will be -(Font) scaled to size 12.24008pt on input line 39. -LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be -(Font) scaled to size 10.20007pt on input line 39. -LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be -(Font) scaled to size 7.65005pt on input line 39. - -Overfull \vbox (13.79993pt too high) detected at line 39 - [] - -[1 - -{/usr/local/texlive/2023/texmf-var/fonts/map/pdftex/updmap/pdftex.map}] -LaTeX Font Info: Trying to load font information for U+msa on input line 53. - - -(/usr/local/texlive/2023/texmf-dist/tex/latex/amsfonts/umsa.fd -File: umsa.fd 2013/01/14 v3.01 AMS symbols A -) -LaTeX Font Info: Trying to load font information for U+msb on input line 53. - - -(/usr/local/texlive/2023/texmf-dist/tex/latex/amsfonts/umsb.fd -File: umsb.fd 2013/01/14 v3.01 AMS symbols B -) -LaTeX Font Info: Font shape `TU/FiraSans(0)/m/it' will be -(Font) scaled to size 8.50006pt on input line 53. -LaTeX Font Info: Font shape `TU/FiraSans(0)/m/it' will be -(Font) scaled to size 5.95004pt on input line 53. -LaTeX Font Info: Font shape `TU/FiraSans(0)/m/it' will be -(Font) scaled to size 4.25003pt on input line 53. -LaTeX Font Info: Font shape `TU/FiraSans(0)/b/n' will be -(Font) scaled to size 10.20007pt on input line 53. -LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be -(Font) scaled to size 3.40002pt on input line 53. - [2 - -] -LaTeX Font Info: Font shape `TU/FiraSans(0)/b/n' will be -(Font) scaled to size 8.50006pt on input line 66. - [3 - -] - -Package fontspec Info: Font family 'FiraSans(13)' created for font 'FiraSans' -(fontspec) with options [Ligatures = TeX,Scale = 0.85,Extension = -(fontspec) .otf,Ligatures=TeX,Numbers = -(fontspec) {Proportional,OldStyle},UprightFont = -(fontspec) *-Regular,ItalicFont = *-Italic,BoldFont = -(fontspec) *-Bold,BoldItalicFont = -(fontspec) *-BoldItalic,Numbers={Monospaced}]. -(fontspec) -(fontspec) This font family consists of the following NFSS -(fontspec) series/shapes: -(fontspec) -(fontspec) - 'normal' (m/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn -;language=dflt;+tlig;+onum;+tnum;" -(fontspec) - 'small caps' (m/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Regular.otf]:mode=node;script=latn -;language=dflt;+tlig;+onum;+tnum;+smcp;" -(fontspec) - 'bold' (b/n) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+onum;+tnum;" -(fontspec) - 'bold small caps' (b/sc) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Bold.otf]:mode=node;script=latn;la -nguage=dflt;+tlig;+onum;+tnum;+smcp;" -(fontspec) - 'italic' (m/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; -language=dflt;+tlig;+onum;+tnum;" -(fontspec) - 'italic small caps' (m/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-Italic.otf]:mode=node;script=latn; -language=dflt;+tlig;+onum;+tnum;+smcp;" -(fontspec) - 'bold italic' (b/it) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+onum;+tnum;" -(fontspec) - 'bold italic small caps' (b/scit) with NFSS spec.: -(fontspec) <->s*[0.85]"[FiraSans-BoldItalic.otf]:mode=node;script=l -atn;language=dflt;+tlig;+onum;+tnum;+smcp;" - -LaTeX Font Info: Font shape `TU/FiraSans(13)/m/n' will be -(Font) scaled to size 7.65005pt on input line 91. -LaTeX Font Info: Font shape `TU/FiraSans(0)/m/n' will be -(Font) scaled to size 5.10004pt on input line 91. -LaTeX Font Info: Font shape `TU/FiraSans(0)/m/it' will be -(Font) scaled to size 7.65005pt on input line 91. -LaTeX Font Info: Font shape `TU/FiraSans(0)/m/it' will be -(Font) scaled to size 5.10004pt on input line 91. -LaTeX Font Info: Font shape `TU/FiraSans(13)/b/n' will be -(Font) scaled to size 7.65005pt on input line 91. - -Overfull \vbox (1.61246pt too high) detected at line 91 - [] - -[4 - -] -\openout4 = iors.vrb - (./iors.vrb) [5 - -] -LaTeX Font Info: Font shape `TU/FiraSans(13)/m/n' will be -(Font) scaled to size 5.95004pt on input line 139. -LaTeX Font Info: Font shape `TU/FiraSans(13)/b/n' will be -(Font) scaled to size 5.95004pt on input line 139. -LaTeX Font Info: Font shape `TU/FiraSans(0)/b/n' will be -(Font) scaled to size 5.95004pt on input line 139. - [6 - -] [7 - -] -\openout4 = iors.vrb - (./iors.vrb) [8 - -] -\openout4 = iors.vrb - (./iors.vrb) [9 - -] -\openout4 = iors.vrb - (./iors.vrb) -[10 - -] [11 - -] -LaTeX Font Info: Font shape `TU/FiraSans(0)/b/n' will be -(Font) scaled to size 7.65005pt on input line 263. - [12 - -] [13 - -] -\openout4 = iors.vrb - (./iors.vrb) [14 - -] [15 - -] [16 - -] -\tf@nav=\write6 - -\openout6 = iors.nav -\tf@toc=\write7 - -\openout7 = iors.toc -\tf@snm=\write8 - -\openout8 = iors.snm - (./iors.aux) -Package rerunfilecheck Info: File `iors.out' has not changed. -(rerunfilecheck) Checksum: D41D8CD98F00B204E9800998ECF8427E;0. -) - -Here is how much of LuaTeX's memory you used: - 31159 strings out of 478285 - 244236,1977958 words of node,token memory allocated - 10339 words of node memory still in use: - 35 hlist, 8 vlist, 5 rule, 75 disc, 6 local_par, 2 math, 185 glue, 185 kern, -19 penalty, 850 glyph, 77 attribute, 71 glue_spec, 77 attribute_list, 4 write, 1 -6 pdf_colorstack nodes - avail lists: 1:3,2:3847,3:247,4:266,5:6816,6:106,7:6156,8:42,9:13512,10:41,11 -:753 - 49861 multiletter control sequences out of 65536+600000 - 147 fonts using 43462351 bytes - 128i,13n,131p,1870b,652s stack positions out of 10000i,1000n,20000p,200000b,200000s - - -Output written on iors.pdf (16 pages, 128936 bytes). - -PDF statistics: 185 PDF objects out of 1000 (max. 8388607) - 131 compressed objects within 2 object streams - 33 named destinations out of 1000 (max. 131072) - 16 words of extra memory for PDF output out of 10000 (max. 100000000) - diff --git a/docs/paper-mods/slides/iors.nav b/docs/paper-mods/slides/iors.nav deleted file mode 100644 index d5ca61a..0000000 --- a/docs/paper-mods/slides/iors.nav +++ /dev/null @@ -1,37 +0,0 @@ -\headcommand {\slideentry {0}{0}{1}{1/1}{}{0}} -\headcommand {\beamer@framepages {1}{1}} -\headcommand {\slideentry {0}{0}{2}{2/2}{}{0}} -\headcommand {\beamer@framepages {2}{2}} -\headcommand {\slideentry {0}{0}{3}{3/3}{}{0}} -\headcommand {\beamer@framepages {3}{3}} -\headcommand {\slideentry {0}{0}{4}{4/4}{}{0}} -\headcommand {\beamer@framepages {4}{4}} -\headcommand {\slideentry {0}{0}{5}{5/5}{}{0}} -\headcommand {\beamer@framepages {5}{5}} -\headcommand {\slideentry {0}{0}{6}{6/6}{}{0}} -\headcommand {\beamer@framepages {6}{6}} -\headcommand {\slideentry {0}{0}{7}{7/7}{}{0}} -\headcommand {\beamer@framepages {7}{7}} -\headcommand {\slideentry {0}{0}{8}{8/8}{}{0}} -\headcommand {\beamer@framepages {8}{8}} -\headcommand {\slideentry {0}{0}{9}{9/9}{}{0}} -\headcommand {\beamer@framepages {9}{9}} -\headcommand {\slideentry {0}{0}{10}{10/10}{}{0}} -\headcommand {\beamer@framepages {10}{10}} -\headcommand {\slideentry {0}{0}{11}{11/11}{}{0}} -\headcommand {\beamer@framepages {11}{11}} -\headcommand {\slideentry {0}{0}{12}{12/12}{}{0}} -\headcommand {\beamer@framepages {12}{12}} -\headcommand {\slideentry {0}{0}{13}{13/13}{}{0}} -\headcommand {\beamer@framepages {13}{13}} -\headcommand {\slideentry {0}{0}{14}{14/14}{}{0}} -\headcommand {\beamer@framepages {14}{14}} -\headcommand {\slideentry {0}{0}{15}{15/15}{}{0}} -\headcommand {\beamer@framepages {15}{15}} -\headcommand {\slideentry {0}{0}{16}{16/16}{}{0}} -\headcommand {\beamer@framepages {16}{16}} -\headcommand {\beamer@partpages {1}{16}} -\headcommand {\beamer@subsectionpages {1}{16}} -\headcommand {\beamer@sectionpages {1}{16}} -\headcommand {\beamer@documentpages {16}} -\headcommand {\gdef \inserttotalframenumber {15}} diff --git a/docs/paper-mods/slides/iors.out b/docs/paper-mods/slides/iors.out deleted file mode 100644 index e69de29..0000000 diff --git a/docs/paper-mods/slides/iors.pdf b/docs/paper-mods/slides/iors.pdf index 5712224230e99da6f3d5a6e57ee4fa0a45e4f74c..d05d24fe744a3f7122ce8c8b306beaecb93a61c6 100644 GIT binary patch literal 299317 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�m^3H11kRG5r|C|K_5 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%0A3l%YlDyq-S4j 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

, - indexes: &[usize], -) -> Result>, Error> { - let paths = indexes - .iter() - .map(|x_t| td.generate_proof(*x_t)) - .collect::>, Error>>()?; - Ok(paths) +/// Encode `witnesses` into codewords. Returns the codewords (one per +/// witness). Each codeword has length `code.code_len()`. +pub fn encode_codewords>( + code: &C, + witnesses: &[Vec], +) -> Vec> { + witnesses.iter().map(|w| code.encode(w)).collect() } diff --git a/src/lib.rs b/src/lib.rs index 8b57bf8..3bd8d4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,11 +3,8 @@ use crate::error::WARPError; use crate::traits::AccumulationScheme; use crate::types::{AccumulatorInstance, AccumulatorWitness, ProveResult, WARPParams, WARPProof}; use ark_codes::traits::LinearCode; -use ark_crypto_primitives::{ - crh::{CRHScheme, TwoToOneCRHScheme}, - merkle_tree::{Config, MerkleTree}, -}; use ark_ff::{Field, PrimeField}; +use ark_mt::MerkleHasher; use ark_poly::{DenseMultilinearExtension, Polynomial}; use ark_std::log2; use config::WARPConfig; @@ -33,6 +30,7 @@ pub mod traits; pub mod types; pub mod utils; +use crate::crypto::merkle::warp_scheme; use error::{DeciderError, ProverError, VerifierError}; use protocol::phases::{ batching::{Batching, BatchingProverInputs, BatchingStatement, BatchingVerifierInputs}, @@ -60,43 +58,37 @@ impl BoolResult for bool { } } -pub struct WARP, C: LinearCode + Clone, MT: Config> { - pub params: WARPParams, +pub struct WARP, C: LinearCode + Clone, H: MerkleHasher> { + pub params: WARPParams, } -impl< - F: Field, - P: Clone + BundledPESAT, // m, n, k - C: LinearCode + Clone, - MT: Config + From<[u8; 32]>>, - > WARP +impl WARP +where + F: Field, + P: Clone + BundledPESAT, + C: LinearCode + Clone, + H: MerkleHasher>, { - pub fn new( - config: WARPConfig, - code: C, - p: P, - mt_leaf_hash_params: ::Parameters, - mt_two_to_one_hash_params: ::Parameters, - ) -> WARP { + pub fn new(config: WARPConfig, code: C, p: P, hasher: H) -> WARP { Self { params: WARPParams { _f: PhantomData, config, code, p, - mt_leaf_hash_params, - mt_two_to_one_hash_params, + hasher, }, } } } -impl< - F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - P: Clone + BundledPESAT, Config = (usize, usize, usize)>, // m, n, k - C: LinearCode + Clone, - MT: Config + From<[u8; 32]>>, - > AccumulationScheme for WARP +impl AccumulationScheme for WARP +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + P: Clone + BundledPESAT, Config = (usize, usize, usize)>, + C: LinearCode + Clone, + H: MerkleHasher>, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, { type Index = P; type ProverKey = (P, usize, usize, usize); @@ -109,8 +101,6 @@ impl< index: Self::Index, ) -> spongefish::VerificationResult<(Self::ProverKey, Self::VerifierKey)> { let (m, n, k) = index.config(); - // initialize prover state for fs - // TODO for R1CS prover_state.public_message(&index.description()); prover_state.prover_message(&F::from(m as u32)); prover_state.prover_message(&F::from(n as u32)); @@ -125,9 +115,9 @@ impl< prover_state: &mut ProverState, witnesses: Self::Witnesses, instances: Self::Instances, - acc_instance: AccumulatorInstance, - acc_witness: AccumulatorWitness, - ) -> ProveResult { + acc_instance: AccumulatorInstance, + acc_witness: AccumulatorWitness, + ) -> ProveResult { debug_assert!(instances.len() > 1); debug_assert_eq!(witnesses.len(), instances.len()); debug_assert_eq!(acc_witness.td.len(), acc_instance.rt.len()); @@ -137,29 +127,32 @@ impl< debug_assert_eq!(l1 + l2, l); debug_assert!(l.is_power_of_two()); - // Parse phase: dimensions and transcript priming. #[allow(non_snake_case)] let (M, N, k) = (pk.1, pk.2, pk.3); let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); - let log_n = log2(self.params.code.code_len()) as usize; + let n = self.params.code.code_len(); + let log_n = log2(n) as usize; debug_assert_eq!(instances[0].len(), N - k); absorb_instances(prover_state, &instances); acc_instance.absorb_into(prover_state); - // Destructure acc_witness so we can hand .f/.w to twin_constraint by - // reference and move .td/.f into proximity afterwards. + // Destructure acc_witness. Codewords live inside td[i].codewords(); + // we extract the single codeword per accumulator entry into acc_fs + // for use by twin_constraint. let AccumulatorWitness { td: acc_tds, - f: acc_fs, w: acc_ws, } = acc_witness; + let acc_fs: Vec> = acc_tds + .iter() + .map(|td| td.codewords()[0].clone()) + .collect(); - // Phase 2: PESAT — emit oracles (codewords), commit, squeeze τs. - let pesat_phase = Pesat:: { + // Phase 2: PESAT + let pesat_phase = Pesat:: { code: &self.params.code, - mt_leaf_hash_params: &self.params.mt_leaf_hash_params, - mt_two_to_one_hash_params: &self.params.mt_two_to_one_hash_params, + hasher: &self.params.hasher, _phantom: PhantomData, }; let (pesat_red, pesat_out) = pesat_phase.prove( @@ -171,8 +164,8 @@ impl< (), )?; - // Phase 3a: twin-constraint sumcheck. - let tc_phase = TwinConstraint:: { + // Phase 3a: twin-constraint sumcheck + let tc_phase = TwinConstraint:: { r1cs: self.params.p.constraints(), _phantom: PhantomData, }; @@ -197,8 +190,7 @@ impl< }, )?; - // Phase 3b: bundled η, ν₀, new commitment, absorb — the "emit new - // oracle + claims" step between twin-constraint and OOD. + // Phase 3b: bundled η, ν₀, new commitment, absorb. let beta_eq_evals = (0..M) .map(|i| eq_poly(&tc_red.beta_tau, i)) .collect::>(); @@ -214,25 +206,19 @@ impl< let new_w = new_w.to_vec(); let new_beta = (vec![tc_red.beta_tau.clone()], vec![new_x]); - let td = { + // Commit to the new (single-codeword) reduced oracle. + let td_new = { let _s = tracing::info_span!("warp.commit_new_oracle").entered(); count_ops!(MerkleTreeBuilds); - MerkleTree::::new( - &self.params.mt_leaf_hash_params, - &self.params.mt_two_to_one_hash_params, - tc_out.f.evals().chunks(1).collect::>(), - )? + let scheme = warp_scheme::(self.params.hasher.clone(), n); + let new_codeword = tc_out.f.evals().to_vec(); + scheme.commit(&[new_codeword]) }; - let td_root_bytes: [u8; 32] = td - .root() - .as_ref() - .try_into() - .expect("root must be 32 bytes"); - prover_state.prover_message(&td_root_bytes); + prover_state.prover_message(td_new.root()); prover_state.prover_message(&eta); prover_state.prover_message(&nu_0); - // Phase 3c: OOD — point queries on the oracle. + // Phase 3c: OOD let ood_phase = Ood::::new(); let (ood_red, _) = ood_phase.prove( prover_state, @@ -244,10 +230,10 @@ impl< OodProverInputs { oracle: &tc_out.f }, )?; - // Sample shift queries — ordering-coupled to the batching ξ below. + // Sample shift queries. let queries = QueryIndices::::sample(prover_state, log_n, self.params.config.t); - // Phase 3d: batching sumcheck. Assemble zetas = [ζ₀, ood_j…, query_k…]. + // Phase 3d: batching sumcheck. let mut zetas: Vec> = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); zetas.push(tc_red.zeta_0.clone()); @@ -272,13 +258,9 @@ impl< BatchingProverInputs { oracle: &tc_out.f }, )?; - // Phase 3e: proximity — index queries + auth paths on accumulated + - // fresh oracles (NOT on the reduced `f`, which is this round's new - // oracle). - let all_codewords: Vec> = acc_fs.into_iter().chain(pesat_out.codewords).collect(); - let proximity_phase = Proximity:: { - mt_leaf_hash_params: &self.params.mt_leaf_hash_params, - mt_two_to_one_hash_params: &self.params.mt_two_to_one_hash_params, + // Phase 3e: proximity. + let proximity_phase = Proximity:: { + hasher: &self.params.hasher, _phantom: PhantomData, }; let (_, prox) = proximity_phase.prove( @@ -287,12 +269,12 @@ impl< queries: queries.clone(), l2, t: self.params.config.t, + n, }, (), ProximityProverInputs { td_0: &pesat_out.td_0, acc_td: &acc_tds, - all_codewords: &all_codewords, }, )?; @@ -302,19 +284,18 @@ impl< nus.extend(ood_red.answers); let new_acc_instance = AccumulatorInstance { - rt: vec![td.root()], + rt: vec![td_new.root().clone()], alpha: vec![batching_red.alpha], mu: vec![batching_out.mu], beta: new_beta, eta: vec![eta], }; let new_acc_witness = AccumulatorWitness { - td: vec![td], - f: vec![tc_out.f.into_evals()], + td: vec![td_new], w: vec![new_w], }; let proof = WARPProof { - rt_0: pesat_out.td_0.root(), + rt_0: pesat_out.td_0.root().clone(), mu_i: pesat_red.mus, nu_0, nu_i: nus, @@ -330,8 +311,8 @@ impl< &self, vk: Self::VerifierKey, verifier_state: &mut VerifierState<'a>, - acc_instance: AccumulatorInstance, - proof: WARPProof, + acc_instance: AccumulatorInstance, + proof: WARPProof, ) -> Result<(), VerifierError> { let (l1, l) = (self.params.config.l1, self.params.config.l); let l2 = l - l1; @@ -342,12 +323,9 @@ impl< let n = self.params.code.code_len(); let log_n = log2(n) as usize; - // 1. Parse statement (fresh instances + accumulator from transcript). - // The acc_instance argument carries the new accumulator's α/β for the - // final consistency checks; the transcript-parsed `parsed_acc` carries - // the OLD l2 accumulator entries used during proximity / β-consistency. + // 1. Parse statement let (l1_xs, parsed_acc) = - parse_statement::(verifier_state, l1, l2, N - k, log_n, log_m)?; + parse_statement::(verifier_state, l1, l2, N - k, log_n, log_m)?; let acc_alpha_first = acc_instance.alpha[0].clone(); let acc_beta_0_first = acc_instance.beta.0[0].clone(); let acc_beta_1_first = acc_instance.beta.1[0].clone(); @@ -356,11 +334,10 @@ impl< let l2_taus = parsed_acc.beta.0.clone(); let l2_xs = parsed_acc.beta.1.clone(); - // 2. PESAT::verify — read rt_0, l1_mus; squeeze l1_taus. - let pesat_phase = Pesat:: { + // 2. PESAT::verify + let pesat_phase = Pesat:: { code: &self.params.code, - mt_leaf_hash_params: &self.params.mt_leaf_hash_params, - mt_two_to_one_hash_params: &self.params.mt_two_to_one_hash_params, + hasher: &self.params.hasher, _phantom: PhantomData, }; let (pesat_red, pesat_v_out) = @@ -369,8 +346,8 @@ impl< let l1_taus = pesat_red.taus; let rt_0 = pesat_v_out.rt_0; - // 3. TwinConstraint::verify — squeeze ω, τ; run sumcheck. - let tc_phase = TwinConstraint:: { + // 3. TwinConstraint::verify + let tc_phase = TwinConstraint:: { r1cs: self.params.p.constraints(), _phantom: PhantomData, }; @@ -387,16 +364,15 @@ impl< (), )?; - // 4. Orchestrator-side reads BETWEEN twin-constraint and OOD: td, η, ν₀. - let _td_bytes: [u8; 32] = verifier_state.prover_message()?; + // 4. Read between TC and OOD: td (new commitment), η, ν₀. + let _td_digest: H::Digest = verifier_state.prover_message()?; let eta: F = verifier_state.prover_message()?; let nu_0: F = verifier_state.prover_message()?; - // 5. Discharge the typed deferred oracle check from TwinConstraint. - // Equivalent to: final_claim ≟ eq(τ, γ) · (ν₀ + ω·η). + // 5. Discharge the deferred oracle check. tc_red.deferred.discharge(&tc_red.gamma, nu_0, eta)?; - // 6. OOD::verify — squeeze samples; read answers. + // 6. OOD::verify let ood_phase = Ood::::new(); let (ood_red, _) = ood_phase.verify( verifier_state, @@ -407,8 +383,7 @@ impl< (), )?; - // 7. Orchestrator: squeeze shift-query byte challenges, build - // QueryIndices, run Proximity::verify. + // 7. Squeeze shift-query bytes; build queries; Proximity::verify. let n_shift_query_bytes = (self.params.config.t * log_n).div_ceil(8); let bytes_shift_queries: Vec = (0..n_shift_query_bytes) .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) @@ -416,9 +391,8 @@ impl< let queries: QueryIndices = QueryIndices::from_squeezed_bytes(&bytes_shift_queries, log_n, self.params.config.t); - let proximity_phase = Proximity:: { - mt_leaf_hash_params: &self.params.mt_leaf_hash_params, - mt_two_to_one_hash_params: &self.params.mt_two_to_one_hash_params, + let proximity_phase = Proximity:: { + hasher: &self.params.hasher, _phantom: PhantomData, }; proximity_phase.verify( @@ -427,6 +401,7 @@ impl< queries: queries.clone(), l2, t: self.params.config.t, + n, }, ProximityVerifierInputs { rt_0: &rt_0, @@ -434,12 +409,10 @@ impl< auth_0: &proof.auth_0, auth_j: &proof.auth_j, shift_query_answers: &proof.shift_query_answers, - _phantom: PhantomData, }, )?; - // 8. Compute ν_{s+k} from the (now proximity-verified) shift answers - // and the γ-equality table; assemble the full nus vector. + // 8. Compute ν_{s+k} from shift answers; assemble nus. let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &tc_red.gamma); let mut nus = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); nus.push(nu_0); @@ -452,8 +425,7 @@ impl< nus.push(nu_st); } - // 9. Build zetas_prefix; Batching::verify squeezes ξ, computes σ₂, - // runs sumcheck, and does the final-claim oracle check. + // 9. Build zetas_prefix; Batching::verify. let mut zetas: Vec> = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); zetas.push(tc_red.zeta_0.clone()); @@ -480,8 +452,7 @@ impl< }, )?; - // 10. Accumulator consistency checks for the new code / circuit - // evaluation points. + // 10. Accumulator consistency. (acc_alpha_first == batching_red.alpha).ok_or_err(VerifierError::CodeEvaluationPoint)?; let betas = l2_taus @@ -499,30 +470,36 @@ impl< fn decide( &self, - acc_witness: AccumulatorWitness, - acc_instance: AccumulatorInstance, + acc_witness: AccumulatorWitness, + acc_instance: AccumulatorInstance, ) -> Result<(), WARPError> { - let computed_mt = MerkleTree::::new( - &self.params.mt_leaf_hash_params, - &self.params.mt_two_to_one_hash_params, - acc_witness.f[0].chunks(1).collect::>(), - )?; - (acc_instance.rt[0] == computed_mt.root()).ok_or_err(DeciderError::MerkleRoot)?; - (acc_witness.td[0].root() == computed_mt.root()).ok_or_err(DeciderError::MerkleTrapDoor)?; + // The accumulator witness's codeword is td[0].codewords()[0]. + let acc_codeword = &acc_witness.td[0].codewords()[0]; + // Re-encode the witness; check codeword match. + let computed_f = self.params.code.encode(&acc_witness.w[0]); + (acc_codeword == &computed_f).ok_or_err(DeciderError::EncodedWitness)?; + + // Re-commit and check root. + let scheme = warp_scheme::(self.params.hasher.clone(), self.params.code.code_len()); + let recomputed = scheme.commit(&[computed_f.clone()]); + (acc_instance.rt[0] == *recomputed.root()).ok_or_err(DeciderError::MerkleRoot)?; + (acc_witness.td[0].root() == recomputed.root()) + .ok_or_err(DeciderError::MerkleTrapDoor)?; + + // MLE evaluation check. let f_hat = DenseMultilinearExtension::from_evaluations_slice( log2(self.params.code.code_len()) as usize, - &acc_witness.f[0], + acc_codeword, ); (f_hat.evaluate(&acc_instance.alpha[0]) == acc_instance.mu[0]) .ok_or_err(DeciderError::MLExtensionEvaluation)?; + // Bundled-evaluation check. let tau = &acc_instance.beta.0[0]; - let tau_zero_evader = Ascending::new(tau.len()) .map(|p| eq_poly(tau, p.index)) .collect::>(); - let mut z = acc_instance.beta.1[0].clone(); z.extend(acc_witness.w[0].clone()); let computed_eta = self @@ -532,9 +509,6 @@ impl< .unwrap(); (computed_eta == acc_instance.eta[0]).ok_or_err(DeciderError::BundledEvaluation)?; - let computed_f = self.params.code.encode(&acc_witness.w[0]); - (acc_witness.f[0] == computed_f).ok_or_err(DeciderError::EncodedWitness)?; - Ok(()) } } diff --git a/src/protocol/phases/pesat.rs b/src/protocol/phases/pesat.rs index 7f7b325..4b23549 100644 --- a/src/protocol/phases/pesat.rs +++ b/src/protocol/phases/pesat.rs @@ -1,31 +1,28 @@ //! PESAT Reduction phase. //! -//! Paired spec: `docs/paper-mods/mod1_oracle.tex` (oracle composition). //! Implements Phase 2 of the Warp prover: encode fresh witnesses into -//! codewords, commit via an interleaved Merkle tree, absorb commitment and -//! code evaluations, and derive the τ zero-check challenges. +//! codewords, commit via a multi-vector Merkle tree (one root over all +//! l1 codewords), absorb commitment + code evaluations, and derive the +//! τ zero-check challenges. //! //! IOR signature //! ------------- //! - `Statement` — `(l1, log_m)` //! - `Witness` — `&[Vec]` (fresh witnesses to encode) -//! - `ProverInputs` — `()` (PESAT is the source — no upstream oracles) +//! - `ProverInputs` — `()` //! - `VerifierInputs` — `()` //! - `ReducedStatement` — `(mus, taus)` — code-eval claims + zero-check randomness -//! - `ProverOutputs` — full codewords + Merkle tree (\(\Oracle{u}\) bundle) +//! - `ProverOutputs` — full codewords + multi-vector commit //! - `VerifierOutputs` — Merkle root only use ark_codes::traits::LinearCode; -use ark_crypto_primitives::{ - crh::{CRHScheme, TwoToOneCRHScheme}, - merkle_tree::{Config, MerkleTree}, -}; use ark_ff::{Field, PrimeField}; +use ark_mt::MerkleHasher; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; use crate::count_ops; -use crate::crypto::merkle::build_codeword_leaves; +use crate::crypto::merkle::{encode_codewords, warp_scheme, WarpCommitted}; use crate::error::{ProverError, VerifierError}; use crate::protocol::phases::IOR; @@ -43,42 +40,45 @@ pub struct PesatReducedStatement { pub taus: Vec>, } -pub struct PesatProverOutputs { +pub struct PesatProverOutputs +where + F: Field, + H: MerkleHasher>, +{ pub codewords: Vec>, - pub td_0: MerkleTree, + pub td_0: WarpCommitted, } -pub struct PesatVerifierOutputs { - pub rt_0: MT::InnerDigest, +pub struct PesatVerifierOutputs { + pub rt_0: H::Digest, } -/// PESAT phase configuration. Holds the linear code and merkle hash -/// parameters borrowed from the enclosing `WARP` struct. -pub struct Pesat<'a, F, C, MT> +/// PESAT phase configuration. +pub struct Pesat<'a, F, C, H> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, C: LinearCode, - MT: Config + From<[u8; 32]>>, + H: MerkleHasher>, { pub code: &'a C, - pub mt_leaf_hash_params: &'a ::Parameters, - pub mt_two_to_one_hash_params: &'a ::Parameters, - pub _phantom: PhantomData<(F, MT)>, + pub hasher: &'a H, + pub _phantom: PhantomData<(F, H)>, } -impl<'a, F, C, MT> IOR for Pesat<'a, F, C, MT> +impl<'a, F, C, H> IOR for Pesat<'a, F, C, H> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, C: LinearCode, - MT: Config + From<[u8; 32]>>, + H: MerkleHasher>, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { type Statement = PesatStatement; type Witness = PesatWitness<'a, F>; type ProverInputs = (); type VerifierInputs = (); type ReducedStatement = PesatReducedStatement; - type ProverOutputs = PesatProverOutputs; - type VerifierOutputs = PesatVerifierOutputs; + type ProverOutputs = PesatProverOutputs; + type VerifierOutputs = PesatVerifierOutputs; #[tracing::instrument( name = "pesat", @@ -93,35 +93,27 @@ where _inputs: Self::ProverInputs, ) -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError> { // a. encode witnesses - let (codewords, leaves) = { + let codewords = { let _s = tracing::info_span!("pesat.encode").entered(); count_ops!(EncodeCalls, witness.witnesses.len() as u64); - build_codeword_leaves(self.code, witness.witnesses, statement.l1) + encode_codewords(self.code, witness.witnesses) }; // b. evaluation claims let mus = codewords.iter().map(|f| f[0]).collect::>(); - // c. commit to witnesses + // c. commit to interleaved codewords (multi-vector commitment) let td_0 = { let _s = tracing::info_span!("pesat.merkle_commit").entered(); count_ops!(MerkleTreeBuilds); - MerkleTree::::new( - self.mt_leaf_hash_params, - self.mt_two_to_one_hash_params, - leaves.chunks_exact(statement.l1).collect::>(), - )? + let scheme = warp_scheme(self.hasher.clone(), self.code.code_len()); + scheme.commit(&codewords) }; // d. absorb commitment + claims; e/f. derive τ challenges. let taus = { let _s = tracing::info_span!("pesat.absorb_and_derive").entered(); - let root_bytes: [u8; 32] = td_0 - .root() - .as_ref() - .try_into() - .expect("root must be 32 bytes"); - prover_state.prover_message(&root_bytes); + prover_state.prover_message(td_0.root()); prover_state.prover_messages(&mus); (0..statement.l1) @@ -150,8 +142,7 @@ where _inputs: Self::VerifierInputs, ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> { // commitment digest - let rt_0_bytes: [u8; 32] = verifier_state.prover_message()?; - let rt_0: MT::InnerDigest = rt_0_bytes.into(); + let rt_0: H::Digest = verifier_state.prover_message()?; // mus (l1 evaluation claims) let mus: Vec = verifier_state.prover_messages_vec(statement.l1)?; diff --git a/src/protocol/phases/proximity.rs b/src/protocol/phases/proximity.rs index f5887ff..34b3351 100644 --- a/src/protocol/phases/proximity.rs +++ b/src/protocol/phases/proximity.rs @@ -1,37 +1,29 @@ //! Proximity / shift-query phase. //! -//! Paired spec: `docs/paper-mods/mod1_oracle.tex` — index queries on the -//! committed oracles. Generates the authentication paths for each shift -//! query leaf against both the fresh PESAT commitment and each accumulated -//! commitment, and collects the codeword values at those leaves. -//! -//! The query indices themselves are sampled from the transcript **before** -//! this phase — see the orchestrator in `src/lib.rs` — so the batching -//! sumcheck can consume the same indices. Proximity itself does not interact -//! with the transcript: it produces proof artifacts (auth paths + answers), -//! which are then attached to the proof on the prover side and verified -//! against transcript-derived commitments on the verifier side. +//! Index queries on the committed oracles. Opens both the fresh PESAT +//! commitment and each accumulated commitment at the query positions, +//! producing one multi-opening proof per commitment and a flat table of +//! the codeword values at those positions. //! //! IOR signature //! ------------- -//! - `Statement` — `(queries, l2, t)` +//! - `Statement` — `(queries, l2, t, n)`. `n` is the codeword length; +//! the verifier needs it to construct the `WarpScheme` +//! used for `check`. //! - `Witness` — `()` -//! - `ProverInputs` — fresh + accumulated merkle trees + all codewords (full data) -//! - `VerifierInputs` — fresh + accumulated commitments + auth paths + answers +//! - `ProverInputs` — fresh `WarpCommitted` + l2 accumulated `WarpCommitted`s +//! - `VerifierInputs` — fresh root + l2 acc roots + opening proofs + answers //! - `ReducedStatement` — `()` (Proximity is a check, not a reduction) -//! - `ProverOutputs` — auth paths + shift_query_answers (proof artifacts) +//! - `ProverOutputs` — opening proofs + shift_query_answers (proof artifacts) //! - `VerifierOutputs` — `()` -use ark_crypto_primitives::{ - crh::{CRHScheme, TwoToOneCRHScheme}, - merkle_tree::{Config, MerkleTree, Path}, -}; use ark_ff::Field; +use ark_mt::{multi_vector::MultiVectorOpening, MerkleHasher}; use spongefish::{ProverState, VerifierState}; use std::marker::PhantomData; use crate::count_ops; -use crate::crypto::merkle::compute_auth_paths; +use crate::crypto::merkle::{warp_scheme, WarpCommitted, WarpProof}; use crate::error::{ProverError, VerifierError}; use crate::protocol::phases::IOR; use crate::protocol::query::QueryIndices; @@ -41,50 +33,64 @@ pub struct ProximityStatement { pub queries: QueryIndices, pub l2: usize, pub t: usize, + /// Codeword length (`code.code_len()`), needed by both prover and + /// verifier to construct the `WarpScheme` used for open/check. + pub n: usize, } -pub struct ProximityProverInputs<'a, F: Field, MT: Config> { - pub td_0: &'a MerkleTree, - pub acc_td: &'a [MerkleTree], - pub all_codewords: &'a [Vec], +pub struct ProximityProverInputs<'a, F, H> +where + F: Field, + H: MerkleHasher>, +{ + pub td_0: &'a WarpCommitted, + pub acc_td: &'a [WarpCommitted], } -pub struct ProximityVerifierInputs<'a, F: Field, MT: Config> { - pub rt_0: &'a MT::InnerDigest, - pub l2_roots: &'a [MT::InnerDigest], - pub auth_0: &'a [Path], - pub auth_j: &'a [Vec>], +pub struct ProximityVerifierInputs<'a, F: Field, H: MerkleHasher> { + pub rt_0: &'a H::Digest, + pub l2_roots: &'a [H::Digest], + pub auth_0: &'a WarpProof, + pub auth_j: &'a [WarpProof], pub shift_query_answers: &'a [Vec], - pub _phantom: PhantomData, } -pub struct ProximityProverOutputs { - pub auth_0: Vec>, - pub auth_j: Vec>>, +pub struct ProximityProverOutputs +where + F: Field, + H: MerkleHasher, +{ + /// Single multi-opening proof for the fresh PESAT commitment. + pub auth_0: WarpProof, + /// One multi-opening proof per accumulated commitment. + pub auth_j: Vec>, + /// Shift query answers: per-query × per-codeword. + /// Outer length = t (queries). Inner length = (l2 acc + l1 fresh). pub shift_query_answers: Vec>, } -/// Proximity phase configuration. Holds borrowed merkle hash parameters used -/// by the verifier-side `verify` (the prover side doesn't need them — auth -/// paths are generated from the merkle trees passed in via `ProverInputs`). -pub struct Proximity<'a, F: Field, MT: Config> { - pub mt_leaf_hash_params: &'a ::Parameters, - pub mt_two_to_one_hash_params: &'a ::Parameters, +/// Proximity phase configuration. Holds the hasher value used by both +/// `open` (prover side) and `check` (verifier side). +pub struct Proximity<'a, F, H> +where + F: Field, + H: MerkleHasher>, +{ + pub hasher: &'a H, pub _phantom: PhantomData, } -impl<'a, F, MT> IOR for Proximity<'a, F, MT> +impl<'a, F, H> IOR for Proximity<'a, F, H> where F: Field, - MT: Config + 'a, - MT::InnerDigest: 'a, + H: MerkleHasher>, { type Statement = ProximityStatement; type Witness = (); - type ProverInputs = ProximityProverInputs<'a, F, MT>; - type VerifierInputs = ProximityVerifierInputs<'a, F, MT>; + type ProverInputs = ProximityProverInputs<'a, F, H>; + type VerifierInputs = ProximityVerifierInputs<'a, F, H>; type ReducedStatement = (); - type ProverOutputs = ProximityProverOutputs; + type ProverOutputs = ProximityProverOutputs; type VerifierOutputs = (); #[tracing::instrument( @@ -93,7 +99,6 @@ where fields( n_queries = statement.queries.leaf_positions.len(), n_accumulators = inputs.acc_td.len(), - n_codewords = inputs.all_codewords.len(), ) )] fn prove( @@ -105,36 +110,56 @@ where ) -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError> { let leaf_positions = &statement.queries.leaf_positions; + // ark-mt's `open()` requires strictly-sorted, unique indices. Query + // positions can repeat or arrive unsorted; deduplicate for the + // merkle opening, but keep `shift_query_answers` in original query + // order so downstream phases (batching) can index by query. + let mut sorted_unique = leaf_positions.clone(); + sorted_unique.sort_unstable(); + sorted_unique.dedup(); + + let scheme = warp_scheme(self.hasher.clone(), statement.n); + let auth_0 = { let _s = tracing::info_span!("proximity.auth_0").entered(); - count_ops!(MerklePathsGenerated, leaf_positions.len() as u64); - compute_auth_paths(inputs.td_0, leaf_positions)? + count_ops!(MerklePathsGenerated, sorted_unique.len() as u64); + scheme.open(inputs.td_0, &sorted_unique) }; - let auth_j = { + let auth_j: Vec> = { let _s = tracing::info_span!("proximity.auth_j").entered(); count_ops!( MerklePathsGenerated, - (inputs.acc_td.len() * leaf_positions.len()) as u64 + (inputs.acc_td.len() * sorted_unique.len()) as u64 ); inputs .acc_td .iter() - .map(|td| compute_auth_paths(td, leaf_positions)) - .collect::>>, _>>()? + .map(|td| scheme.open(td, &sorted_unique)) + .collect() }; + // Shift query answers: per query position, values across + // (acc_codewords ++ fresh_codewords). let shift_query_answers = { let _s = tracing::info_span!("proximity.shift_queries").entered(); + let total_codewords = + inputs.acc_td.iter().map(|td| td.num_codewords()).sum::() + + inputs.td_0.num_codewords(); let mut answers = - vec![vec![F::default(); inputs.all_codewords.len()]; leaf_positions.len()]; - for (i, idx) in leaf_positions.iter().enumerate() { - let row = inputs - .all_codewords - .iter() - .map(|f| f[*idx]) - .collect::>(); - answers[i] = row; + vec![vec![F::default(); total_codewords]; leaf_positions.len()]; + for (qi, idx) in leaf_positions.iter().enumerate() { + let mut col = 0usize; + for td in inputs.acc_td.iter() { + for cw in td.codewords() { + answers[qi][col] = cw[*idx]; + col += 1; + } + } + for cw in inputs.td_0.codewords() { + answers[qi][col] = cw[*idx]; + col += 1; + } } answers }; @@ -164,34 +189,49 @@ where (inputs.shift_query_answers.len() == statement.t) .ok_or_err(VerifierError::NumShiftQueries)?; - - for (i, path) in inputs.auth_0.iter().enumerate() { - (path.leaf_index == leaf_positions[i]).ok_or_err(VerifierError::ShiftQueryIndex)?; - count_ops!(MerklePathsVerified); - let is_valid = path.verify( - self.mt_leaf_hash_params, - self.mt_two_to_one_hash_params, - inputs.rt_0, - &inputs.shift_query_answers[i][statement.l2..], - )?; - is_valid.ok_or_err(VerifierError::ShiftQuery)?; - } - (inputs.auth_j.len() == statement.l2).ok_or_err(VerifierError::NumL2Instances)?; - for (i, paths) in inputs.auth_j.iter().enumerate() { - (paths.len() == statement.t).ok_or_err(VerifierError::NumShiftQueries)?; - let root = &inputs.l2_roots[i]; - for (j, path) in paths.iter().enumerate() { - (path.leaf_index == leaf_positions[j]).ok_or_err(VerifierError::ShiftQueryIndex)?; - count_ops!(MerklePathsVerified); - let is_valid = path.verify( - self.mt_leaf_hash_params, - self.mt_two_to_one_hash_params, - root, - [inputs.shift_query_answers[j][i]], - )?; - is_valid.ok_or_err(VerifierError::ShiftQuery)?; - } + + // Build the (sorted, unique) positions used by the merkle openings + // and remember which row of `shift_query_answers` corresponds to + // each unique position. Duplicate queries land on the same row. + let mut indexed: Vec<(usize, usize)> = leaf_positions + .iter() + .copied() + .enumerate() + .map(|(row, pos)| (pos, row)) + .collect(); + indexed.sort_by_key(|&(pos, _)| pos); + indexed.dedup_by_key(|&mut (pos, _)| pos); + let sorted_unique: Vec = indexed.iter().map(|&(p, _)| p).collect(); + let row_indices: Vec = indexed.iter().map(|&(_, r)| r).collect(); + + let scheme = warp_scheme::(self.hasher.clone(), statement.n); + + // Fresh PESAT opening: per unique position, take the l1-suffix of the + // corresponding answers row. + let fresh_values: Vec> = row_indices + .iter() + .map(|&r| inputs.shift_query_answers[r][statement.l2..].to_vec()) + .collect(); + let fresh_opening = MultiVectorOpening::new(sorted_unique.clone(), fresh_values) + .map_err(|_| VerifierError::ShiftQueryIndex)?; + scheme + .check(inputs.rt_0, &fresh_opening, inputs.auth_0) + .ok_or_err(VerifierError::ShiftQuery)?; + count_ops!(MerklePathsVerified, sorted_unique.len() as u64); + + // Accumulator openings: each is m=1 (single codeword). + for (k, root) in inputs.l2_roots.iter().enumerate() { + let acc_values: Vec> = row_indices + .iter() + .map(|&r| vec![inputs.shift_query_answers[r][k]]) + .collect(); + let acc_opening = MultiVectorOpening::new(sorted_unique.clone(), acc_values) + .map_err(|_| VerifierError::ShiftQueryIndex)?; + scheme + .check(root, &acc_opening, &inputs.auth_j[k]) + .ok_or_err(VerifierError::ShiftQuery)?; + count_ops!(MerklePathsVerified, sorted_unique.len() as u64); } Ok(((), ())) diff --git a/src/protocol/phases/twin_constraint.rs b/src/protocol/phases/twin_constraint.rs index c787b24..e8ce067 100644 --- a/src/protocol/phases/twin_constraint.rs +++ b/src/protocol/phases/twin_constraint.rs @@ -28,8 +28,8 @@ //! - `ProverOutputs` — the new reduced oracle `f` + the reduced witness vector `z` //! - `VerifierOutputs` — `()` (the new commitment is read from the transcript by the orchestrator) -use ark_crypto_primitives::merkle_tree::Config; use ark_ff::{Field, PrimeField}; +use ark_mt::MerkleHasher; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; use effsc::{ coefficient_sumcheck::RoundPolyEvaluator, @@ -173,8 +173,8 @@ impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { // ─── IOR signature types ────────────────────────────────────────────────── -pub struct TwinConstraintStatement { - pub acc_instance: AccumulatorInstance, +pub struct TwinConstraintStatement { + pub acc_instance: AccumulatorInstance, pub l1_mus: Vec, pub l1_taus: Vec>, pub log_l: usize, @@ -249,21 +249,21 @@ pub struct TwinConstraintProverOutputs { } /// TwinConstraint phase configuration. -pub struct TwinConstraint<'a, F, MT> +pub struct TwinConstraint<'a, F, H> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - MT: Config + From<[u8; 32]>>, + H: MerkleHasher, { pub r1cs: &'a R1CSConstraints, - pub _phantom: PhantomData, + pub _phantom: PhantomData, } -impl<'a, F, MT> IOR for TwinConstraint<'a, F, MT> +impl<'a, F, H> IOR for TwinConstraint<'a, F, H> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - MT: Config + From<[u8; 32]>>, + H: MerkleHasher, { - type Statement = TwinConstraintStatement; + type Statement = TwinConstraintStatement; type Witness = TwinConstraintWitness<'a, F>; type ProverInputs = TwinConstraintProverInputs<'a, F>; type VerifierInputs = (); diff --git a/src/protocol/transcript/prover.rs b/src/protocol/transcript/prover.rs index c755b0b..b6f5442 100644 --- a/src/protocol/transcript/prover.rs +++ b/src/protocol/transcript/prover.rs @@ -1,8 +1,8 @@ use ark_ff::Field; +use ark_mt::MerkleHasher; use spongefish::{Encoding, ProverState}; use crate::types::AccumulatorInstance; -use ark_crypto_primitives::merkle_tree::Config; // absorb a list of plain instances into the transcript pub fn absorb_instances>( @@ -17,15 +17,15 @@ pub fn absorb_instances>( } // absorb an AccumulatorInstance into the transcript -impl< - F: Field + Encoding<[u8]>, - MT: Config + From<[u8; 32]>>, - > AccumulatorInstance +impl AccumulatorInstance +where + F: Field + Encoding<[u8]>, + H: MerkleHasher, + H::Digest: Encoding<[u8]>, { pub fn absorb_into(&self, prover_state: &mut ProverState) { for digest in &self.rt { - let bytes: [u8; 32] = digest.as_ref().try_into().expect("digest must be 32 bytes"); - prover_state.prover_message(&bytes); + prover_state.prover_message(digest); } for alpha in &self.alpha { diff --git a/src/protocol/transcript/verifier.rs b/src/protocol/transcript/verifier.rs index 6e06c1e..0bea026 100644 --- a/src/protocol/transcript/verifier.rs +++ b/src/protocol/transcript/verifier.rs @@ -1,41 +1,42 @@ -use ark_crypto_primitives::merkle_tree::Config; use ark_ff::Field; -use ark_std::log2; - +use ark_mt::MerkleHasher; use spongefish::{Decoding, Encoding, NargDeserialize, VerificationResult, VerifierState}; use crate::types::AccumulatorInstance; // (l1 instances, accumulated instance) -pub type ParsedStatement = (Vec>, AccumulatorInstance); +pub type ParsedStatement = (Vec>, AccumulatorInstance); // parse l1 plain instances + an AccumulatorInstance from the transcript -pub fn parse_statement< - F: Field + NargDeserialize + Encoding<[u8]> + Decoding<[u8]>, - MT: Config + From<[u8; 32]>>, ->( +pub fn parse_statement( verifier_state: &mut VerifierState<'_>, l1: usize, l2: usize, instance_len: usize, log_n: usize, log_m: usize, -) -> VerificationResult> { +) -> VerificationResult> +where + F: Field + NargDeserialize + Encoding<[u8]> + Decoding<[u8]>, + H: MerkleHasher, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, +{ let l1_xs: Vec> = (0..l1) .map(|_| verifier_state.prover_messages_vec(instance_len)) .collect::>()?; let acc = - AccumulatorInstance::::parse_from(verifier_state, l2, log_n, log_m, instance_len)?; + AccumulatorInstance::::parse_from(verifier_state, l2, log_n, log_m, instance_len)?; Ok((l1_xs, acc)) } // parse an AccumulatorInstance from the verifier transcript -impl< - F: Field + NargDeserialize + Encoding<[u8]> + Decoding<[u8]>, - MT: Config + From<[u8; 32]>>, - > AccumulatorInstance +impl AccumulatorInstance +where + F: Field + NargDeserialize + Encoding<[u8]> + Decoding<[u8]>, + H: MerkleHasher, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, { pub fn parse_from( verifier_state: &mut VerifierState<'_>, @@ -44,11 +45,8 @@ impl< log_m: usize, instance_len: usize, ) -> VerificationResult { - let rt: Vec = (0..l2) - .map(|_| -> VerificationResult<_> { - let bytes: [u8; 32] = verifier_state.prover_message()?; - Ok(bytes.into()) - }) + let rt: Vec = (0..l2) + .map(|_| verifier_state.prover_message::()) .collect::>()?; let alpha: Vec> = (0..l2) @@ -76,112 +74,3 @@ impl< }) } } - -/// Transcript values read BEFORE the twin-constraint sumcheck: PESAT -/// commitment + l1 mus + l1 τs + ω + τ. -pub struct PreTwinConstraint { - pub rt_0: MT::InnerDigest, - pub l1_mus: Vec, - pub l1_taus: Vec>, - pub omega: F, - pub tau: Vec, -} - -/// Transcript values read BETWEEN the two sumchecks: new commitment, -/// η, ν₀, OOD samples+answers, shift-query byte challenges, ξ. -pub struct BetweenSumchecks { - pub td: MT::InnerDigest, - pub eta: F, - pub nus: Vec, - pub ood_samples: Vec, - pub bytes_shift_queries: Vec, - pub xi: Vec, -} - -pub fn derive_pre_twin_constraint< - F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, - MT: Config + From<[u8; 32]>>, ->( - verifier_state: &mut VerifierState<'_>, - l1: usize, - log_l: usize, - log_m: usize, -) -> VerificationResult> { - // commitment digest - let rt_0_bytes: [u8; 32] = verifier_state.prover_message()?; - let rt_0: MT::InnerDigest = rt_0_bytes.into(); - - // mus - let l1_mus: Vec = verifier_state.prover_messages_vec(l1)?; - - // challenge taus (squeezed) - let l1_taus: Vec> = (0..l1) - .map(|_| { - (0..log_m) - .map(|_| verifier_state.verifier_message::()) - .collect() - }) - .collect(); - - let omega: F = verifier_state.verifier_message(); - let tau: Vec = (0..log_l) - .map(|_| verifier_state.verifier_message::()) - .collect(); - - Ok(PreTwinConstraint { - rt_0, - l1_mus, - l1_taus, - omega, - tau, - }) -} - -pub fn derive_between_sumchecks< - F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, - MT: Config + From<[u8; 32]>>, ->( - verifier_state: &mut VerifierState<'_>, - log_n: usize, - s: usize, - t: usize, -) -> VerificationResult> { - // td digest - let td_bytes: [u8; 32] = verifier_state.prover_message()?; - let td: MT::InnerDigest = td_bytes.into(); - - // eta and nu_0 - let eta: F = verifier_state.prover_message()?; - let nu_0: F = verifier_state.prover_message()?; - let mut nus = vec![nu_0]; - - // ood samples - let n_ood_samples = s * log_n; - let ood_samples: Vec = (0..n_ood_samples) - .map(|_| verifier_state.verifier_message::()) - .collect(); - - // ood answers - let ood_answers: Vec = verifier_state.prover_messages_vec(s)?; - nus.extend(ood_answers); - - // shift queries and ξ - let r = 1 + s + t; - let log_r = log2(r) as usize; - let n_shift_queries = (t * log_n).div_ceil(8); - let bytes_shift_queries: Vec = (0..n_shift_queries) - .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) - .collect(); - let xi: Vec = (0..log_r) - .map(|_| verifier_state.verifier_message::()) - .collect(); - - Ok(BetweenSumchecks { - td, - eta, - nus, - ood_samples, - bytes_shift_queries, - xi, - }) -} diff --git a/src/serialize.rs b/src/serialize.rs index 84d159a..9f0e7db 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -1,50 +1,44 @@ -use ark_crypto_primitives::merkle_tree::{Config, Path}; use ark_ff::{Field, PrimeField}; +use ark_mt::MerkleHasher; use ark_serialize::CanonicalSerialize; +use crate::crypto::merkle::WarpProof; use crate::types::{AccumulatorInstance, AccumulatorWitness, WARPProof}; #[derive(CanonicalSerialize)] -pub struct AccWitnessSerializer< - F: Field + PrimeField, - MT: Config + From<[u8; 32]>>, -> { - pub f: Vec, +pub struct AccWitnessSerializer { pub w: Vec, - _mt: std::marker::PhantomData, + _h: std::marker::PhantomData, } -impl + From<[u8; 32]>>> - AccWitnessSerializer -{ - pub fn new(acc_witness: AccumulatorWitness) -> Self { +impl>> AccWitnessSerializer { + pub fn new(acc_witness: AccumulatorWitness) -> Self { assert_eq!(acc_witness.td.len(), 1); - assert_eq!(acc_witness.f.len(), 1); assert_eq!(acc_witness.w.len(), 1); Self { - f: acc_witness.f.into_iter().next().unwrap(), w: acc_witness.w.into_iter().next().unwrap(), - _mt: std::marker::PhantomData, + _h: std::marker::PhantomData, } } } #[derive(CanonicalSerialize)] -pub struct AccInstanceSerializer< - F: Field + PrimeField, - MT: Config + From<[u8; 32]>>, -> { - pub rt: MT::InnerDigest, +pub struct AccInstanceSerializer +where + H::Digest: CanonicalSerialize, +{ + pub rt: H::Digest, pub alpha: Vec, pub mu: F, pub beta: (Vec, Vec), pub eta: F, } -impl + From<[u8; 32]>>> - AccInstanceSerializer +impl AccInstanceSerializer +where + H::Digest: CanonicalSerialize, { - pub fn new(acc_instance: AccumulatorInstance) -> Self { + pub fn new(acc_instance: AccumulatorInstance) -> Self { assert_eq!(acc_instance.rt.len(), 1); assert_eq!(acc_instance.alpha.len(), 1); assert_eq!(acc_instance.mu.len(), 1); @@ -66,23 +60,26 @@ impl + Fr } #[derive(CanonicalSerialize)] -pub struct ProofSerializer< - F: Field + PrimeField, - MT: Config + From<[u8; 32]>>, -> { - pub rt_0: MT::InnerDigest, +pub struct ProofSerializer +where + H::Digest: CanonicalSerialize, + WarpProof: CanonicalSerialize, +{ + pub rt_0: H::Digest, pub mu_i: Vec, pub nu_0: F, pub nu_i: Vec, - pub auth_0: Vec>, - pub auth_j: Vec>>, + pub auth_0: WarpProof, + pub auth_j: Vec>, pub f_i_x_j: Vec>, } -impl + From<[u8; 32]>>> - ProofSerializer +impl ProofSerializer +where + H::Digest: CanonicalSerialize, + WarpProof: CanonicalSerialize, { - pub fn new(proof: WARPProof) -> Self { + pub fn new(proof: WARPProof) -> Self { Self { rt_0: proof.rt_0, mu_i: proof.mu_i, diff --git a/src/traits.rs b/src/traits.rs index 7f0b8f2..484bbad 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,11 +1,11 @@ -use ark_crypto_primitives::merkle_tree::Config; use ark_ff::Field; +use ark_mt::MerkleHasher; use spongefish::{ProverState, VerificationResult, VerifierState}; use crate::error::{VerifierError, WARPError}; use crate::types::{AccumulatorInstance, AccumulatorWitness, ProveResult, WARPProof}; -pub trait AccumulationScheme { +pub trait AccumulationScheme>> { type Index; type ProverKey; type VerifierKey; @@ -25,21 +25,21 @@ pub trait AccumulationScheme { prover_state: &mut ProverState, witnesses: Self::Witnesses, instances: Self::Instances, - acc_instance: AccumulatorInstance, - acc_witness: AccumulatorWitness, - ) -> ProveResult; + acc_instance: AccumulatorInstance, + acc_witness: AccumulatorWitness, + ) -> ProveResult; fn verify<'a>( &self, vk: Self::VerifierKey, verifier_state: &mut VerifierState<'a>, - acc_instance: AccumulatorInstance, - proof: WARPProof, + acc_instance: AccumulatorInstance, + proof: WARPProof, ) -> Result<(), VerifierError>; fn decide( &self, - acc_witness: AccumulatorWitness, - acc_instance: AccumulatorInstance, + acc_witness: AccumulatorWitness, + acc_instance: AccumulatorInstance, ) -> Result<(), WARPError>; } diff --git a/src/types.rs b/src/types.rs index 4db018d..13a1e0c 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,40 +1,39 @@ use ark_codes::traits::LinearCode; -use ark_crypto_primitives::{ - crh::{CRHScheme, TwoToOneCRHScheme}, - merkle_tree::{Config, MerkleTree, Path}, -}; use ark_ff::Field; +use ark_mt::MerkleHasher; use std::marker::PhantomData; use crate::config::WARPConfig; +use crate::crypto::merkle::{WarpCommitted, WarpProof}; use crate::error::ProverError; use crate::relations::BundledPESAT; // result of a prove call: (new accumulator instance + witness, proof) -pub type ProveResult = Result< +pub type ProveResult = Result< ( - (AccumulatorInstance, AccumulatorWitness), - WARPProof, + (AccumulatorInstance, AccumulatorWitness), + WARPProof, ), ProverError, >; /// Protocol parameters for WARP — the shared configuration used by all IOR phases. -pub struct WARPParams, C: LinearCode + Clone, MT: Config> { +pub struct WARPParams, C: LinearCode + Clone, H: MerkleHasher> { pub _f: PhantomData, pub config: WARPConfig, pub code: C, pub p: P, - pub mt_leaf_hash_params: ::Parameters, - pub mt_two_to_one_hash_params: ::Parameters, + /// The hasher value (replaces the old leaf-hash + two-to-one-hash parameter pair). + pub hasher: H, } + /// Accumulator instance — the public part of an accumulated claim. /// /// Corresponds to `(rt, α, μ, (τ, x), η)` in the paper. #[derive(Clone)] -pub struct AccumulatorInstance { +pub struct AccumulatorInstance { /// Merkle tree root commitments. - pub rt: Vec, + pub rt: Vec, /// Code evaluation points (one per accumulated oracle). pub alpha: Vec>, /// Code evaluation targets (one per accumulated oracle). @@ -45,7 +44,7 @@ pub struct AccumulatorInstance { pub eta: Vec, } -impl AccumulatorInstance { +impl AccumulatorInstance { pub fn empty() -> Self { Self { rt: vec![], @@ -59,22 +58,41 @@ impl AccumulatorInstance { /// Accumulator witness — the private part of an accumulated claim. /// -/// Corresponds to `(td, f, w)` in the paper. -#[derive(Clone)] -pub struct AccumulatorWitness { - /// Merkle tree trapdoors (full trees). - pub td: Vec>, - /// Oracle evaluations (codewords). - pub f: Vec>, +/// Corresponds to `(td, w)` in the paper. The codewords are stored +/// inside `td[i].codewords()` so we don't carry a parallel `f` field. +pub struct AccumulatorWitness +where + F: Field, + H: MerkleHasher>, +{ + /// Committed multi-vector trees (with the codewords cached inside). + pub td: Vec>, /// R1CS witnesses. pub w: Vec>, } -impl AccumulatorWitness { +impl Clone for AccumulatorWitness +where + F: Field, + H: MerkleHasher>, + WarpCommitted: Clone, +{ + fn clone(&self) -> Self { + Self { + td: self.td.clone(), + w: self.w.clone(), + } + } +} + +impl AccumulatorWitness +where + F: Field, + H: MerkleHasher>, +{ pub fn empty() -> Self { Self { td: vec![], - f: vec![], w: vec![], } } @@ -83,32 +101,57 @@ impl AccumulatorWitness { /// Proof produced by the WARP accumulation prover. /// /// Corresponds to `(rt₀, μᵢ, ν₀, νᵢ, auth₀, authⱼ, f_i(x_j))` in the paper. -#[derive(Clone)] -pub struct WARPProof { +pub struct WARPProof { /// Fresh Merkle tree root. - pub rt_0: MT::InnerDigest, + pub rt_0: H::Digest, /// Fresh code evaluations at 0. pub mu_i: Vec, /// Evaluation of accumulated oracle at zeta_0. pub nu_0: F, /// Evaluation claims (OOD + shift query answers). pub nu_i: Vec, - /// Authentication paths for the fresh commitment. - pub auth_0: Vec>, - /// Authentication paths for each accumulated commitment. - pub auth_j: Vec>>, + /// Single multi-opening proof for the fresh PESAT commitment, covering + /// all queried indices across all `l1` interleaved codewords. + pub auth_0: WarpProof, + /// One multi-opening proof per accumulated oracle (each is a single + /// codeword tree, opened at the same `t` query positions). + pub auth_j: Vec>, /// Shift query answers: `f_i(x_j)` for each query position `j` and oracle `i`. pub shift_query_answers: Vec>, } +impl Clone for WARPProof +where + F: Field, + H: MerkleHasher, + H::Digest: Clone, + WarpProof: Clone, +{ + fn clone(&self) -> Self { + Self { + rt_0: self.rt_0.clone(), + mu_i: self.mu_i.clone(), + nu_0: self.nu_0, + nu_i: self.nu_i.clone(), + auth_0: self.auth_0.clone(), + auth_j: self.auth_j.clone(), + shift_query_answers: self.shift_query_answers.clone(), + } + } +} + /// Intermediate output of the PESAT reduction phase. /// /// This data flows from Phase 2 (PESAT Reduction) into Phase 3 (Constrained Code Accumulation). -pub struct PesatOutput { +pub struct PesatOutput +where + F: Field, + H: MerkleHasher>, +{ /// Encoded codewords from fresh witnesses. pub codewords: Vec>, - /// Merkle tree over the interleaved codeword leaves. - pub td_0: MerkleTree, + /// Multi-vector commitment over the `l1` codewords. + pub td_0: WarpCommitted, /// Code evaluation claims: `f_i(0)` for each codeword. pub mus: Vec, /// PESAT evaluation challenges (one per fresh instance). diff --git a/tests/integration_warp.rs b/tests/integration_warp.rs index 17086cf..d79abfa 100644 --- a/tests/integration_warp.rs +++ b/tests/integration_warp.rs @@ -20,8 +20,8 @@ use ark_codes::{ traits::LinearCode, }; use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; -use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; use ark_ff::UniformRand; +use ark_mt::blake3::Blake3FieldHasher; use ark_serialize::{CanonicalSerialize, Compress}; use ark_std::rand::thread_rng; @@ -85,17 +85,16 @@ fn warp_test() { .unwrap(); let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = WARP::, _, Blake3MerkleConfig>::new( + let hash_chain_warp = WARP::, _, Blake3FieldHasher>::new( warp_config.clone(), code.clone(), r1cs.clone(), - (), - (), + Blake3FieldHasher::::new(), ); let (mut acc_roots, mut acc_alphas, mut acc_mus, mut acc_taus, mut acc_xs, mut acc_eta) = (vec![], vec![], vec![], vec![], vec![], vec![]); - let (mut acc_tds, mut acc_f, mut acc_ws) = (vec![], vec![], vec![]); + let (mut acc_tds, mut acc_ws) = (vec![], vec![]); for _ in 0..l1 { let domainsep = spongefish::domain_separator!("test::warp"); @@ -118,7 +117,6 @@ fn warp_test() { acc_eta.push(acc_x.eta[0]); acc_tds.push(acc_w.td[0].clone()); - acc_f.push(acc_w.f[0].clone()); acc_ws.push(acc_w.w[0].clone()); } @@ -126,12 +124,11 @@ fn warp_test() { let warp_config = WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = WARP::, _, Blake3MerkleConfig>::new( + let hash_chain_warp = WARP::, _, Blake3FieldHasher>::new( warp_config.clone(), code.clone(), r1cs.clone(), - (), - (), + Blake3FieldHasher::::new(), ); let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); @@ -150,7 +147,6 @@ fn warp_test() { }, AccumulatorWitness { td: acc_tds, - f: acc_f, w: acc_ws, }, ) @@ -174,8 +170,8 @@ fn warp_test() { .decide(acc_w.clone(), acc_x.clone()) .unwrap(); - let acc_x_to_serde = AccInstanceSerializer::<_, Blake3MerkleConfig>::new(acc_x); - let acc_w_to_serde = AccWitnessSerializer::<_, Blake3MerkleConfig>::new(acc_w); + let acc_x_to_serde = AccInstanceSerializer::<_, Blake3FieldHasher>::new(acc_x); + let acc_w_to_serde = AccWitnessSerializer::<_, Blake3FieldHasher>::new(acc_w); let proof_to_serde = ProofSerializer::new(pf); println!( @@ -242,17 +238,16 @@ fn warp_test_goldilocks() { let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); let hash_chain_warp = - WARP::, _, Blake3MerkleConfig>::new( + WARP::, _, Blake3FieldHasher>::new( warp_config.clone(), code.clone(), r1cs.clone(), - (), - (), + Blake3FieldHasher::::new(), ); let (mut acc_roots, mut acc_alphas, mut acc_mus, mut acc_taus, mut acc_xs, mut acc_eta) = (vec![], vec![], vec![], vec![], vec![], vec![]); - let (mut acc_tds, mut acc_f, mut acc_ws) = (vec![], vec![], vec![]); + let (mut acc_tds, mut acc_ws) = (vec![], vec![]); for _ in 0..l1 { let domainsep = spongefish::domain_separator!("test::warp"); @@ -275,7 +270,6 @@ fn warp_test_goldilocks() { acc_eta.push(acc_x.eta[0]); acc_tds.push(acc_w.td[0].clone()); - acc_f.push(acc_w.f[0].clone()); acc_ws.push(acc_w.w[0].clone()); } @@ -285,12 +279,11 @@ fn warp_test_goldilocks() { WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); let hash_chain_warp = - WARP::, _, Blake3MerkleConfig>::new( + WARP::, _, Blake3FieldHasher>::new( warp_config.clone(), code.clone(), r1cs.clone(), - (), - (), + Blake3FieldHasher::::new(), ); let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); @@ -309,7 +302,6 @@ fn warp_test_goldilocks() { }, AccumulatorWitness { td: acc_tds, - f: acc_f, w: acc_ws, }, ) @@ -333,8 +325,8 @@ fn warp_test_goldilocks() { .decide(acc_w.clone(), acc_x.clone()) .unwrap(); - let acc_x_to_serde = AccInstanceSerializer::<_, Blake3MerkleConfig>::new(acc_x); - let acc_w_to_serde = AccWitnessSerializer::<_, Blake3MerkleConfig>::new(acc_w); + let acc_x_to_serde = AccInstanceSerializer::<_, Blake3FieldHasher>::new(acc_x); + let acc_w_to_serde = AccWitnessSerializer::<_, Blake3FieldHasher>::new(acc_w); let proof_to_serde = ProofSerializer::new(pf); println!( diff --git a/tests/profile_json.rs b/tests/profile_json.rs index 5a7d6ff..2aa78dc 100644 --- a/tests/profile_json.rs +++ b/tests/profile_json.rs @@ -16,7 +16,7 @@ use ark_codes::{ traits::LinearCode, }; use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; -use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; +use ark_mt::blake3::Blake3FieldHasher; use ark_std::rand::thread_rng; use ark_std::UniformRand; use std::marker::PhantomData; @@ -96,12 +96,11 @@ fn json_layer_emits_phase_records() { .unzip(); let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = WARP::, _, Blake3MerkleConfig>::new( + let hash_chain_warp = WARP::, _, Blake3FieldHasher>::new( warp_config, code, r1cs.clone(), - (), - (), + Blake3FieldHasher::::new(), ); let domainsep = spongefish::domain_separator!("test::profile_json"); diff --git a/tests/verifier_negative.rs b/tests/verifier_negative.rs index 06ddef8..302a300 100644 --- a/tests/verifier_negative.rs +++ b/tests/verifier_negative.rs @@ -28,7 +28,7 @@ use ark_codes::{ traits::LinearCode, }; use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; -use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; +use ark_mt::blake3::Blake3FieldHasher; use ark_std::rand::thread_rng; use ark_std::UniformRand; @@ -47,24 +47,24 @@ use warp::utils::poseidon; use warp::WARP; type F = BLS12_381; -type MT = Blake3MerkleConfig; -type WarpT = WARP, ReedSolomon, MT>; +type H = Blake3FieldHasher; +type WarpT = WARP, ReedSolomon, H>; /// Everything the verifier needs to re-check, plus enough dimensions /// to re-derive the verifier state. struct Fixture { warp: WarpT, vk: (usize, usize, usize), - acc_x: AccumulatorInstance, - proof: WARPProof, + acc_x: AccumulatorInstance, + proof: WARPProof, narg_str: Vec, } impl Fixture { fn verify( &self, - acc_x: AccumulatorInstance, - proof: WARPProof, + acc_x: AccumulatorInstance, + proof: WARPProof, ) -> Result<(), VerifierError> { let domainsep_v = spongefish::domain_separator!("test::warp::negative"); let mut verifier_state = domainsep_v @@ -118,11 +118,16 @@ fn make_fixture() -> Fixture { // Phase 1: produce `l1` single-round acc states so we have a non-trivial // accumulator to feed phase 2 (l2 > 0 so NumL2Instances is reachable). let warp_cfg1 = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); - let w1 = WARP::, _, MT>::new(warp_cfg1, code.clone(), r1cs.clone(), (), ()); + let w1 = WARP::, _, H>::new( + warp_cfg1, + code.clone(), + r1cs.clone(), + Blake3FieldHasher::::new(), + ); let (mut roots, mut alphas, mut mus, mut taus, mut xs, mut etas) = (vec![], vec![], vec![], vec![], vec![], vec![]); - let (mut tds, mut fs, mut ws) = (vec![], vec![], vec![]); + let (mut tds, mut ws) = (vec![], vec![]); for _ in 0..l1 { let ds = spongefish::domain_separator!("test::warp::negative"); @@ -144,13 +149,17 @@ fn make_fixture() -> Fixture { xs.push(acc_x.beta.1[0].clone()); etas.push(acc_x.eta[0]); tds.push(acc_w.td[0].clone()); - fs.push(acc_w.f[0].clone()); ws.push(acc_w.w[0].clone()); } // Phase 2: the "real" prove with l2 > 0 accumulated instances. let warp_cfg2 = WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); - let warp = WARP::, _, MT>::new(warp_cfg2, code, r1cs.clone(), (), ()); + let warp = WARP::, _, H>::new( + warp_cfg2, + code, + r1cs.clone(), + Blake3FieldHasher::::new(), + ); let ds = spongefish::domain_separator!("test::warp::negative"); let mut ps = ds.without_session().instance(&0u32).std_prover(); @@ -169,7 +178,6 @@ fn make_fixture() -> Fixture { }, AccumulatorWitness { td: tds, - f: fs, w: ws, }, ) @@ -240,23 +248,6 @@ fn truncated_shift_query_answers_raises_num_shift_queries() { assert_err(fix.verify(fix.acc_x.clone(), proof), "NumShiftQueries"); } -#[test] -fn swapped_auth0_leaf_index_raises_shift_query_index() { - let fix = make_fixture(); - let mut proof = fix.proof.clone(); - // Overwrite auth_0[0]'s path with auth_0[1]'s path so leaf_index - // stops matching queries.leaf_positions[0]. - let p0_is_p1 = proof.auth_0[0].leaf_index == proof.auth_0[1].leaf_index; - if p0_is_p1 { - // Extremely unlikely but keeps the test deterministic: skip with a - // clear message rather than silently pass. - eprintln!("fixture happened to sample identical leaf indices; skipping"); - return; - } - proof.auth_0.swap(0, 1); - assert_err(fix.verify(fix.acc_x.clone(), proof), "ShiftQueryIndex"); -} - #[test] fn tampered_shift_query_answer_raises_shift_query() { let fix = make_fixture(); From 7aca28956d60d77b00488ba071246256b8eba4f5 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Mon, 4 May 2026 21:19:11 +0200 Subject: [PATCH 25/68] ark-vc HashRegions --- .cargo/config.toml | 2 ++ Cargo.toml | 4 ++-- docs/paper-mods/slides/iors.pdf | Bin 299317 -> 311229 bytes docs/paper-mods/slides/iors.tex | 30 ++++++++++++------------------ src/crypto/merkle/mod.rs | 9 +++------ src/lib.rs | 10 +++------- src/protocol/phases/proximity.rs | 15 ++++++++------- tests/verifier_negative.rs | 13 +++---------- texput.log | 20 -------------------- 9 files changed, 33 insertions(+), 70 deletions(-) create mode 100644 .cargo/config.toml delete mode 100644 texput.log diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..c91c3f3 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[net] +git-fetch-with-cli = true diff --git a/Cargo.toml b/Cargo.toml index 19b58f3..ea15ed9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,8 +28,8 @@ tracing = { version = "0.1", default-features = false, features = ["std", "attri tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter", "json", "registry"], optional = true } libc = { version = "0.2", optional = true } ark-codes = { git = "https://github.com/z-tech/ark-codes.git", branch = "z-tech/arkworks-0.6.0" } -ark-vc = { path = "../ark_vc/crates/ark-vc" } -ark-mt = { path = "../ark_vc/crates/ark-mt", features = ["blake3", "blake3-field"] } +ark-vc = { git = "https://github.com/arkworks-rs/ark-vc.git", branch = "z-tech/hash-regions" } +ark-mt = { git = "https://github.com/arkworks-rs/ark-vc.git", branch = "z-tech/hash-regions", features = ["blake3", "blake3-field"] } [dev-dependencies] ark-bls12-381 = "0.6.0" diff --git a/docs/paper-mods/slides/iors.pdf b/docs/paper-mods/slides/iors.pdf index d05d24fe744a3f7122ce8c8b306beaecb93a61c6..811f5f33680333dc2c86fe80f1360a5b1430d404 100644 GIT binary patch delta 206197 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%0AAcud_lFEn*Sc##$ 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�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%0A`x^rq(B);0h3(%NX^! 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

1bHw{|7Oo|c3;bEjj;vrro;g?_myJ~YYHY&! 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 WH4#9H#>K(T%??7Qp^^R~gZw|DSD!`z delta 195082 zcmY(pQ*wiE&S(3nL0a|j78^XxF4|J_%8^3j)zw*n}WecC@%28Ai6_B9h>nDfqGR5Cs zaxAb+%jq;!#Faw8@D zL7`jN;N+@s zLvADnt|-_1KKRcb8QVpcxR}I3U>mjJqp-QsX@a249?3c~EZsLO^r_;#_0_V>qj|r0 zUf*S(Hk+K00VgD`(FmB6PI){W+9-cICCyB=rN0pFQ~8}o4jleY4wo_Ms0v@8?^Mdj z$|S|EFo9-(UyxuB`@*edrGOUGhO}o}GU7Vx7_{q&N?Z%R6J#tE%!1JA_w%FIV$XIb zml2nj>0Nn0o|Wq0aIv*T(30RLXxeCz-}Gd_yt=p(0A|oVqpJ-y1jo0)osJ&8K3Y6U z6p}f@aSC7>2Q=^dHyDbJ^1UA!Awp&wqG*VcM{TuXEHx3FkKexge#4PSTik0NckD5Olm;C)lUvzTR9UM^i{B?!iJz3hY@)UY6__F-75xh zksU{70S*IMm)T|L@0RTLRR%(hBX~=+Q8ku$S9+u}@FnT9Dc@6 zP>&TOQT;$XIS_u#8cFK>pY8F+4bptQbWTDOW)4j3!NEz8kcTI$15~-&C1H_k_55ppJ z7sANs;U*_d3mBWnw1E#N;Sj{m8)7Y$jnI{gDVJVtt!5D~KVS`Xte3H{K0S?12-R8n zrxjsyXe)Kf$a&fqt7+K}CLkU_nd^4&=6$V&xXLYtqkl^Mc^Vy&`K&5}N~;tqne!k5 zb;>1KFOZ&04@nKmodiY?a6RNi=>5=g@~L1~WAT&wt+XxZu`yJtAdoD!7-BlXVr4gx zc;vHaWE5CTx?W}2nnS*LH^MFw2o`w*-cOxYF&C;b;2sqX0cu0EcXK&0^8x9*$muPq z4EpyadJK{*hMJ*5GYO`K<`l<%GeRQaOP2cW2jZ^*zjsrmna2Hvo8g0z(`&hVx zL*bH;t`5u3g0nceIi0KD=+ls4&kti*@QapuLWSrFEafELL2&iLc>uPR=M#gpNEfDY zBgaEf%-w-9_NFPD@0U}jZ=1gU7d6{pVs|k8DtTGOf%td~T+w(Vd7o5U)|$AIGpRZ5 z_UNFvMAu7~S+MRHzKp{f zKWvoolShjDS-!%8HU%@duCP}Vc*~qp%CG*{b0q5OOipm}f4g$|p^xWDMGZb- zKkh)vT*Qjqrw8wsH5Yp5{p8Hgf4tdhj*G6Uk*lA|`-^6aooV7kSiQ=I^OrsO-zpMv zx?p6XFv*Sr@T}EjMY>2RVU{%cGToBX~bh`=Q-H?ai-l4-#J^2fVHj zrQom0&i~VL%Tz2sPO(&P!%{P)lz>J4LMDEb6>zxE@p^QJQ8`L;sZ{$#bwmddH*^(2 z`521=@blECU1f}OkH7*r&xYA(*M*AIPSZdg+nON@;N~w3L8!e_wp3jN_E#|vuk}JT z7uR~m9W37SUU_@Q*xHq^9a%b8{^D?{&ia=e7-4#V#RY;+7m-3K4-FK%(kT7Gr{OEbzH~h+DI6eE4z{It5mAjYOgy@~OL@@>-i-HdhCLBPT}ka+xQjGh2@d zCFtB~*4U_F2(hPdbU1T|NlK0nLoRn*PbLL?a25{cu5Kt)ja`ppy@8Q;%42Gx+wwx9tLg=pv!KfYGtv@Lo$@{Shtl;e$uZYOL+zBn`{9B##C2oxWQ1jUIOiS%!82&Nxv!n%w@VbNZU`?-98UT zI6w1zd@}i`Q)B1?j=K994W$`^)f`uo3#U@4_w%}y!F*kYn(qaB1Wv1S{e7?q=?x29 z*_D%u1UAA{FK%#&F5J2HAkzMW#h@|+M2F6GogdG#MQ_tB0fYk}u*(sk2sI>U12Z;3 zH*PK|t4pmI%C3GCCGRjXX3pAXLusDa-GTcNUZs{uwKxfQTaB3evC(7DfB$z>N0#`m z7DGh?onJ&AUb!V*9D zF=GI~ZFcqPeP?C$!N#oTYUh-F&M1BMxxmGh$frkUpUqQm6U`_{X{STqe%li0Ln^0O zf|9ciPxK5hd_8TfBq^fTS*2N(S^PEjF8Og4TeBF`WR8CCD=it3CLsf-iy1>!9FrEz zYHh_|1~r9LVBeb8sf3?&3vQekpYazz3K#x;PAP);3VmR76($$vRz*~MFAEpVvf!h! zq2X+&qoPT!%Z15w4ze>Y@Ko$rv(=_>uKsZG==2Pbg)v=xBA)K!2>h){Ql3IJ-~J6b zJNoS5Yv$Mm2cB}xzIcIgzjW^d8A`eZQSZSJZi4#1gUkF^ThYMF&;*FO`0zsfy|JY@Q*8ixvb)5D`WEG048}(`y zi3JD5YtR8e1aY$>$HJLCG;nB>+WbsA9)fS8B|K1+PS-d$CFH#+nVdI1ui@~`-mYkx zn1*)^Y~ywD|FX$jQ}gdWRo1RPNu*#~ZgXC=CwavueH|YYEk>iQj5rtsRUfL_q10*- zbDP4fh2JQoCofJ@1UksIq{dfnKH`f-F}nchM~S)`{8?0kWx2C*M$Y;uB>Nl&J&wwO zTPWm7GBUpc*W@1C(#>oy1t1+h(HXRrc`^o73=q|3MA`7q@4OuGuoPeOHpTi{=r&wy z64KOv+M~SWwf`*#(^Au!Tk%|W73N0?;C{NPo<&=;!+2WlPL~m$+PAl8x796XNx+GO zoI{nOt;I$-zjej5^4W62cbBTd`Y$|Y`-}b&rG!jDA=h9|?~)xVW5cZdVM@DaU4KW7N;Aq3fby&f z!YpiH{+?*`(BP5?@Qe6Za8b~!$88tQkN%5lWoh;5|B#OjY0^#_jn{2lzJhP#7>k|rhwd+RRNJ(;pxEra^5q+caP&0Dn>k1^nnHZ8g&dp ziQeAVu_I; zP5(D)ia)=EqcUQOukjacF8FPU)MFx5N2e$&|%*ACu`Cqma0 ziLH`iA8V{U_qGKut5N{qD~RycAvQ$3d&gusLs$lhkGDZKx=ClmUUfuvEOVkUAEtzf z%DuW(1;Z$PaCr~p5UV;meOIX!Xu0oRfu~MzhD3KIb=6K(uMr4HXZr6tX%OV;>W?|C zjz0KQ#--TvNbHu$x-@zj$&nZ_JI^uAQs}1U^OJK-G)eUQ5pMw0hC|KFeb6TjCtw4b zn7+sxxf1UlcXR_m`66#VUo>>Nh4jlPEqjA$Sh9Ahh?nyvx|gwk(WV&er7Z0)@davb z9ubPpwPB8_I@NL?rBNJX(5PbqOr4dSVee+RK-HRljbVtfih8v6N0|D*LMGo}oX2sC zKjQ1Q+!zbXkM@AUPcNO(pnS(oIF~&cM1y|91c?Fh9Y_rBwnpP5{^0ZYRa{uNejwr06s#}p!HY{f1h=W9fJOgfQS$v{rT?*{^{%QQnB`PfSzehGIebAmAAI zGPPVvZ>m#GgXWKWI0IW&pZ5u~FxT8Y0hDlVs{p{L?xyqap3@N-f>@v2o*Z$L>S9fS zi$7-E(>P5$OFm5-=|AYiFoe^V=1RFOptolEy`O^impkpT{ffBr!+WqC}7x z@N_dpB$WJcb9qNxf4=n>wLmq_p@>GM0pUbmG>g0Mt8+r{aXvY6)Z|dVKCZF8Qgb-( z+iTI(nQ@Vr1a7qJfrS*6n`lOwvV(7zqUbIp7u+v5wwH!P)b!eo=z;biuqCG!KsGzX z(W4Sqx8+Hw{)!`7M*oSWaKgF4 z;-A_y1xNS$7bhdit<@uP?cK@;Ls%(k&B0J;W=NcWZyDOpH^;GQu+Gm2h}5o>&MqYs zUq=Z(`{1@C-gd>m27E6DtG{LnkgHhi2wo{ZJ;> zJbg((Xu!{bhg}|b%dK^5jBGDJg_g1Guh1wTDRPgCKQzSLBI1>T)u;^8cF2oVsjnR< zce*d3wh^IHZCYW@DqcqP9tSn2+uM|mkD&3sF5GiyamlNgq53ZXMy6e3(0l)0-o!_L zmiGy_m@>VltFNCCh!Hs1a(woGRSFK9zds1nXJ|x^&FA!bZ~uNX)OvNT2|bOp)VxUW zEooJQJk6whCX;K1$bVeGuGpGCyY8Zj_P%*u=5Cs*E$2TG>HuJo^CwuMU-ReyD8C<< zd%I7;j-hw4fu{NZ9tOXJLE_Ra(`fI{vNU%E8%^90*U1$CDy&la!-7#A?*ZKyoOcPz zx`X&8;S2Nph;)(P$nTHGJx$ZBLe8$|S;6npg{>WJc@x0~%^#xRr-e6P@B^!1UxAP6 zDi8IFOWe=w(Sp9vwSY#@a2GcnqOF)zT%Zx72p-~=7tVu}*TsfxIj$GZ7W?PqT~OmD z45&uO}q4p+vG*PLyGYcgI8FYKlQyZ^!rk@j|v4+%5^AhBv-# z0@qGbHhDJ3e5$sT;zuc?8IJ+JCzjpg-Q%AL09O4`I36&R11gkvUEPA5Jj6)Mc!UoLJ}qI`=??Nv!dBKTO#!A%kl=_f0g!RvtfD92SO>WX4uL zqkcDherrEpnl}5rq2={*F76jHDdT%tD|Tm7Oxk^EAyZc6S?fzbk6oXnt`Somb)6gx zK*y?3vJ8)_-Ws!Z7l5N<1Eeb=u659 zAI{eIjF%h9jLO`39MVTmoPKZPdGo@4K!Sle+@wk=2x7|yyr@JHA~6pofil(1{np3O6k_#o=6qaiQm zy9?Dz5ifeGCx><`q}rWpUY<^|1Z;};n-aIh@#7&1Ez8(rQr6X^o?0|;3qp#856Q7P zsInQlX5;T)#V-f53H}|)p%FTga}zfBH9_KpO21zpw_jM1reJU-Xvv)afnz2PAb(4b zZ0+6)qNAoCoKeu2_xanuu;4B-ZPUOkuF#KMNV`UMTH`1T`L0JeUpz`Q%;u$V(>QVd z$yjKnX6gBy-=u|sq7&yqVE?Dz%BQC_{Oc)Y>3f&e-abAZj+ChC7q=cQ=6ynSccyoc zuhKC$)7iGV<-+uzTptz?9C-ygzy?UZn8^#NrvpzERHgS3BGT`rfVer7lx>jb%|^Nm zxQ0uYQn9>lX9h?iCnr$^MSSDDoInrRL-IM=khh3QVUR;;pOD_fC_c(+7wilWT)RI4 z8*`@XjBwIuSC}>**=9diT5`#jtNP{WnOyxizTCzx8kX);b}qhwH0SpW0LU;^`GH7N zM6G95Ew7z#%-SNGkarR!S{UEh$K*&dg#f)mV-Pk6o zrF2B&_ySYf%x_ZQ1336M@z5#Dm$$ekF^bHOlm62k9GZ5dNDb`^at6@~DPEvY0p>n< zY!gSWvmy~6$2XGkHrC}FC3($;#_+14zH~JT5TA8Ue zSP40NTC=u-jG;n-(Qj^8p-36aVvc%PA`0Ynfy;vw7=f8vzy=ULeM51i1iB<|lKj8w zo4kn+3m|^3R}mI}AiYp62K}3V&52;035PxBL5Hpr7p9X$H(|3_^V4%0nPLn5yKP+r z7a@SfecbiX6H#a(8x})afje0gnwvVE_zA-gu~wg-;11dzEery~*o)VP?;sRSiLO*Y zqIg9_Gwcg5Q2+_33E0B0bE$O~2LGr{)X z8bU38rB6Lhw^zM)$+OCU8rYM{zt)A@g^^lKN2%aFj)*?haCteI1=Ej{hm5$-*l5SH zqgJkVnw-(1JMl2_z5P`3+{8%8Tw>KQ`FFxUWk(nSt`KGLQj6qFaa2@sKr?E5@8=Z3fdAB34?7$PMq+|@Kshos3TvM2 zf$T3cH}x^?{TU<{f29Xg!+PgNum@w( zl;HihAY|k^bGM0tE&;rNC>=ooAt^3BY;bO67i*%$eOT7ymOVRBtd_+~A^H;!0HhdL zI}~jU-5>Gl7>nbxpo?!uo>c9*Hm~DZBQJd=w7}A)pO0C#2;xQr2)BZIA(Sm5q?%* zb0`_q?V}o=trgtVdv=R{&hBU~0gFcqH;WGF$sTREv!&n8r(T}IQXoa@2&n8G{9^Bc zO}Wk98oE)!kIoRYqZ^JcbD22^1Ly{1F;u$tsJT(;p*RlzJpakPZa$zQ=Va-pYAl{q zI5(sg6V)?o`+B)~ZXL=JGLw9D73CXz%w}J@7+q8~ozu?ju>T#3qmnu~0u28JJLILw zYMxEIk^!iXD=U?*b*+?A4cF!sbQP4ux5gPG=@}SUwQ#%#^r>lAZ#b$2cWW~~Gl(Ls zc|(uiK`?iPrAu@eXdP}AS-QseiK^$_;I8fze%V{>c%B$uL5-%H+((EzFyYhKd_IJc z)Mz9>ITCyMd84Ch@)!1f0Unj$gbcF*I-u$FxkwAffdJB;++kg@rv8Sc*@U0U&NFGx zz;Cxt5O9P{vRpARz1vbce zH0{UwD1mXuOHP+w*>L{UaV_4c@q(b!i{}IXzxg|vo)Z86hOqyo!Q2jcbE!ns62Y=kAV&Ho!c`&&s9fEUFipWOf00jQ>FFjNwE@6;)Ob7E)5$A7l(K;r~9s z=u@AgYY|&IbCjX$cs?!YDT7(4=-M266C%m+4g8=f+I~58e|-C}b|IKX9PC@nK26F< z4_6EYr`IR&t;(H_evO#*ZXPIYd$W50r;7$_;2kG}#|;3|O$;ThOsY-g)iYVM{HunS zI|F%kGlF#d`=#DC9t5w5+s&$U@uD%U@?G7vDKFpe5p_#B1VsNvM!;Qi3?%=cC6eFM zc!Z3SKZdbE629(fwwY6qj&UDv&{C>MS27311!jjZ0E6J7B&G;A! zM+gsGe&Yjz2|*?cNMwjZXy)kS7^>jb{b}N+i=PP?5$>O;lP)eVxz<-s@iM+x2A^et zte%`~l3I_SHQuViTrIL}+SL$$)0nC~nRx*=GV7{Gb#)N5)bxS` zoN}ifTiP5n2xoq0@;Vn6$gkoX27ZHBgF@9!=}XeHfEqEHZ?OnghRj&Ol&+bDPAe4e-JNlL#9Dt!3OCEX3I1LW!UUTl>8{~~6MIIMC7yz9+c=#~u8YOW z5$m9ZWS)c`q5xZA)C z8&KGf{gs~PZACSWI}@ zH1!`T5v(e?L;Rs1;262UKA#k)j%D{U**S3mLj>WP1xM zy`pBno7pkeQUO4DmUNPnmuacN*m?eUViFdEYhtD21O*`0zjcNJrJ|ZaRz|go5fFvT zQDEpEG}3(erZ9hv2Rub^h%a;^oCC~_&90Isf@>Qz^Rr)^EUeu&Uz`IF;xkNEX$$YAdcYlhEky6#fc zlbUte*MJVZE}-3G$Q!aY+a+IM25luN$aZG$10>ah5*w9Qu#dPJ4_q&gnXGEi$T-1$ zT9YP35#0MEz#ej++e~61y(}al$nRm8E2ma=_|Ln@xT+Ykg(;*=<1B*u@e<_d@yTQs z678KS?f|bL|6W2VDhenG$hfvP$Tg%DlMmWq9AK!zloo@i`;Jhhj^QhcmD#*5x}Q-! zl@Kh5h7;xr?=bFwe9KaLtr5m=V%#9ac|l#_YxH+WFu}lXSC4}n^Vm7OnbQRhJQz33 z#Tdo=ac$kbdxntq9{o?W;EkJF8+01_yCJhBY79n&6u|j-0qD5c0xsHIBf26NfHiv~H3_Kv+ZG8rQ@*bWTZ( zHUr-Qa`%*_ErfWHMGIeeFF49vp87{f$vDS_24C$JlJyu9jsU^{gaMM9j5-qz-z9(i z>gr!)pvCA4)O?QIB_=m-K)yIb5ZY_;3!k~!( zh;rR?y;6&I9nG9?t1Nnt03#)U-Nts1eaR0zsi2YCxmdXt@HUVJS%@pGfie&9Ic9YMnA^`0 zUdAZ2kn{EE@=ZWy&JbZrJ2g2a34gFXIKYOB4M7lgR0wpU@OK-ExyH!d3<1~)h8Q93 z;?N$>Oi?pYjXhv!Ct0$Z@W^2w{k3kib6y@MjFzWj2bxNl0zd&K&ZcH}z0&)a{OaGO zu-s3J)pT8rwQgw4$88>Gok5PvVg5Yu+%H&lN>`hS{@p*v*AaP2O3Xe|OsUxRpsmo4 z7FbGJmI?&aLNkulZ`NbMN$p&Ws?1$*#dUd|&6ke91}n^%NJ;$0%%*j~3LyUo- zo2SM$C&T6zYfuUi`Sp~(WQ*nog_!oeH6u35Mo1JV0Z=P{Y!q-kB?apMdnG4wXY37=a) z#%(eP#jf6CaYY@-ua~c|DDsq-6D7(|Skx(koScOpE~eUrWzMS$n!E<`MC0Ad z2X{!}uwN&e)xJ)7MlZ10NY)D1{ZHP%66u>%wPs)L)|+5o{y^kM>;k%v+cq_{YA0pO zW`_7mLtVUq3+S?-7QRjMrGc!~omU-IlSJVRm965=L0`sUHHYzdTEp#jS{EzJ-RC0y z-d&G@fu5nREO`I<&`+IsYe z@SuxwHLNtos37U0B{i3JlZG7`hSt z7pbF*i@q>8{(a(wAPXXkiLO91t2R;p&~%QJ1KdbkRIWr)7C@@lmcGIhd_tNRjguu& zMd1+C^<$Vn10S}f0N%Ed%0O!vZf*NDp{@#omS0ttTnHra1K(!&QN-XFvDlV;iKlQy zIUY@RCK6X~?sRmfo8e+MQI}L2Jv?qn-v=05+u!Qmn}-9%0o>;qksi*=x2)1LIO+LI zv(V}^2z%Hd1dwlw-}{=mIImW!6V}+UV{={4F6K*^_ABGsIlmlB6Y|Gs6$mp}g{pXi z!KCa0bbh}+ze@b`foU8>E&!%loo|rTU+st0_nir0o{xUXvu%dyWp969@sS+<6pQS1 z5X~k-$a6Dikj8`$0l#tr1YjaMi2JFugufuo4BC2D0Bu|#?W71`%@3JrH5gQ!Abc6S z9~~Ot<-fuS7ax2!74)Ncjts*2)>1T9yV~n@m_7e|w3*?c;#Tt6*Ou3nEk%()gP=g={oVl3C(13p_jz zN6S<>$$$PEOH(94Gh9-nc#ujK)wEugJ8|wn7V!hZnB`$n%J;R=LLj<_( zKQBXGZg;#KpksR&174U%^vV3l6(*un#{9p&5rpWY7^5UI!+?~iya0MigY{H*3q0Hw zl+*pw7JFGe1=3pM{@9SM(n$GseMx@U8|(T;j}1vc?0;GumLPJrQp{JLrvb@q6QgmdQ6zSlm7l>(1Q9)T>M&O&`ivhinT*?m&y zSsgeYZC8n6myQCa$31KgY(lIz+}zzsU6uuUdWg-xNKj#rG7zwNJ%fw_sg7+~n;Y@&eHYnhSP}Q>d%=4?Intrd@I=~Gk$7fBT z>?Frt@M@qv!lMCO^xMntbY4xpCygl)1p>lnZMp98( zL=RbjG}W_3SBLD}R1 zkCb^&V?X)>K?Lz)m!Y0xCCy1N{Uao*6BwzJHy~4;n?4JnIJo4VaTEVe7zs0n{0^0l zyiMP7?=HdC&u|88DC1;^#4*Uq(v!+VVL&$L*>usxN1nssr|-%zv5$JiwjPCTzRA~j z|EvNHK7$Q(WlWSM_pB(v#r!t`(~2HKD3*DY?8{05st1rk=fz~PkJ>+`9KXGzF0!;TP$s!^9{5WOtK3D2}PslRO!)wi72jiDY1!SGk;nAbNg8N57+UfM zFFFJZ8yDyQsYBb2|4|a~VpPxrfr>f*iJ1xowc?IqJe`_5BBUcW?TqsU{GGt6rZpnOE+%0gvPsNa2+0Wd6 zTKrNF0gmr9kg!LeFG?(`u6%C1@$vO1>yG|`{=r}pV4UePL?UAPqGQ=qu;-#g_| zFp;3l#*I?fcA{F8@qWH&l0O|W3r>Tx!R96l-@ScPzM$;ssalF4gq`NF3ZT)DR>qA< z`{2qGCr#yI?B%0rbKIL*pHit4GN&b`^tNHngrbqF14QWI5P0TqI!;y!6m0wU~X_y9Y>;94WUzIb%(BA zNxMSvyt^YK*znl5n78$7NVy_7w}RaM-Mc&*M*tcZt~IJiqv4KIgT{u5_HTD zjGWKUzgc?gFz$p%^h;+4HMC6xEuKxVB%p|fV7;)Q`hEeY!<<@ zA3l^Fl9MygrVhb2EKHC7Qs`@@QVB+_*fQ9RF=u8vr*V9{8c}!O01JMk%@+DxY4zIW}Kkk$|RJy+y6k9dJ9Czkdkc z$KNkYDYdTlru>;-Yt7%q3ifgVY4r{t7;yIC^$KWMg62Ca!9*BLn@g8&vcjqM(C)pv z?F{y_AEd{BHCrQ^-mJe>tg;z$=GEl%tSRh+&iNK9_IR#P$3QM zXlM9fbPZs74Vpe&5)EuPr=Ls@G8^o7yZ97LLTMZ+{MrIASt3JpPm^1-^7y=4?9WbB zP#qYnwmk3L57BG-YWV)-Q-@()EV`F>RoH=@TxCrN=JO|%_$sA-yD$6AEJhLc`qpKO zv=a2;{K79v;lkF5BA507j1uv-2A?um6dteH&IinJcJfwCK3%-JF`vN@iK2xr+7I`f zt>u!8EdCuZ5Gaxb)|Jzt0s8^UT$Z<*MM>UNYJGRtKAY}!&#Fa(2ET6!JTm*qQA0m% z(G=siEN4^dC^f+*=W7x^icBmn94)74+-L|+`=|1n7&OTM3ssF)=_gHbY+i7Cj&Ds3 zRl6-TQ@zi%#Lo6FOkrs!eAK&3C8md|@)xcVeQBHjB`+8=S2Cy=#{Z<# zWMQ#i0L|vt1#p29YqVmd;%)5lg3S=pDNYn1%AcJpu&=uu5*i(kX6Y=bEc<$_WXcG# z*=a5|A~I3$!D1{FNsD0c2*|q4K{t=n`w>!~IjhDcF;Hwn`W+@xru)z%aky(18rYJG z)zlr!yG?Me!@KFjjJkXIVb&m276qqhN_|#OfVBtE&=Cv%?|^JJOgNu2u8hgQ>1%$; zr@Lvp!~f(YoT9)z^kbD-5t{(uy_!;wkuuUI{x|Di)QNO0^|Bl$O4ewq*qdZV&)<8G zyPj1e_dH4h|H(<+%cE1$q~&8?vfA|B%NrltfTk&TWY*kQE(>9@9~rFrai*&UD6LFJ zfHc|9U~RKPU2`X*YTwr3iT&-^#n`{26E=buFyzDhlt}L5Gvdr7?j*x;@|ZtQb4s9) zwo{l427*`!-RUFrlFpJMF=EoOdsA9znkVWOqNdCf6GuHyW)2aCo)Mx%{iiJSCEq)Y zwht4P+t3UDu87=PqeZsQ&?v%SbrzxX0Z1c*D~#zO3Mk6wT#5}}YPKH(V2{Ou&Ln>A zI^BMZYDil!z_WQ|RH`tLPq#y2-GfaOPdp>g`#U0GF6NHS?bqFJferVa6kslRBuUc2J_Tt+7c_Kviig zAe@DZ7Os1%fENKKIsGL2gGA94zQ}NspsZ_hcOPS0BBiyc<5{z(WwmTxVCWnlmlKiX zm|TH~l-&>%wKHCFePvtVy}85L#8t0@fh9?)H$kR71Y28MEnX_Til?EB z&4-*_jB{D-oQDha6s;(iNw3%j6OFI|TE8^!ir`mxx8ZK*hBXki|Fj!et6@~`Q5OzY z8eS_@;SkY_Um<*aQ9|JYU~V8%<6v$F3co753AvtwPez5>=)NI*F)vr~Eq!T!3X*rI zqN0>E3Ficastu}nfH5EL7ljhv%?}kl&|0iX3VFb>tc_Ty`j=3%e2_V@ zBmF`4lo?0o8-VA)!CgoEZODlb-H`gd=^uFLi&bwfbo|5M{Q-fR#W#A9u#{MN0k#I$ z(wtC4!;J{o*jBDoPMa>ZIQ|ZyY}Lqf5eUh8lg%ZClBn-}wCTF5n1T9GMYuxdoEw6ZvBAWdF$7vSU|2T~X&cVt2|MdF!|1xs#O&zjN zMX03yU5Qj6;R2@uTK?4vOML`6bdCJxpl(vpm;=~&KF)!%x!7r0U?X=k~n# zesHgq>!VJ^^su7GMD{12@Q}u&;dTY@CKk}6W#3O~1W5uo{Ih#AH5-Q*6q;6%2c7cn z$*kPY+^;4+_@gJpEuC*&^EivtlcVt~61!n<`+XU36xqbqD+_}Iqegm=VhdvrJ7)D44EMfFf5yO9d$>W|gNyQLC{rwj0O^n0Z_(3qgVmDtnZv46a{;r(U zOP*-BU1b13lVVLQl4Vz^kc~1RVqZ1RR7tmSFUd##_Y4QS`_nbdF{bLFu}#gjE?{F! zg|4(_Zwskql&70JrE9|)0*_gFOEe9SEmkv0so4}I<$&H*mqV{-4Bop?1=CQPXV@g_ z?#0gisYg;0NXqtO&1mEEVC!Uy=uraPEI5N!KOg~M>WHe&s6TH}X&%?Kw7XDd%LkNm z3Qr#{5g4;8FQaUS(SZ4|f9-g^qfHhPSpHr0Xk3Qkg!Wf=?K_fW{7<81+Xj~Ay^R?v z7okOeA6dZQ@l`-YW-7ljYL+JAHLCxoB4ab3wF|L%Se18;<5!y2Md|VJwOVLAmrOh< zjoJVvrKhFrRvB5_#GoTGzl8U2CQLDbU;N;|J-c=#Q*4U@LW5vLCmtiC!))_sCD}b( zD8o^F0%6GVtGir6R_nkC-(D=fnw*WdJ3h-%HXHd&>_pRL$`LZ)_osK%P9x)G;OPqd z?@b<4MAV%ue^Ztv@OcK+LoQpJE1=i~gOmW!kq>7vTedw$8b|o?7zE|cbhnwZ5gJCD zhW}J&7VPMAM8yBQxgSb+QBKBGl|!agZ1?#1JVUKq!U3Bd_)iHD>}yzJndmDOea+XO z2%Vu5W_zTH*T);)D0;@#Q5MXUa@^So_Z$(ecUAP_rA+ZCXDs5IlU!Y58B}mt#cKiR zz-^F2T{}yi@rLneURN9W5*Q&yR1{e8mZ8Wjpg7gI%m?s4E1a=Pxhv&qR`v*J*ZTG5 zx*_ow1olYjh>|9rVIItMjc@jE(9OgOhrL)@9XnIk@(v;H*jiH|y#4#ig&9P(hR(l{ zCV2I)`um$EWawaiU%_799@7qUn)U7!)k*$TCuS@$-a_ z8p=QUxf>y76wd1+?}4FtzVcqiQ`uSVx!s(S8JtbT7t>R9s{72*?Ta}M6D0?^kn{EPWG&{W;_kCu(4Rqt<=gi?PGcpBi%vu z)XD~wX(`i&#}?=6prwkB{KQi=K%#&(7DAY z1&+8Q8qyX)2>qdRrO&rhj>tQqBdwohxC<#f4xWnq|(%>lTwwK&=vMC;`v0i*g z~9af z|KnnBbkL>N_V)V!0XRU%zkkE(t08`r&WMHzB6VjEbw>hAS+0?nfr#Mo=pB@S#_Aty z+`0Xi0jL5K0y8$3a3%p1lWk8912Z@|m+^E16a_UjHZm}m0e=K1lfF-P2DX%Ndk~%tzKuDLSonNf4Om)2|RKj4}YCR74nahf|J%=iRDL>R3 z(s^aJy%60Fpx5apj_9ia+UEoZmYNHHn}-Gj^iA-L_`g? zh0)}m ze@F|JM>4qJp;F)h75i~hQclkzt^#6(j4ZjeZ-NKI#Yxbu%o2ZDGKHXZid`Pg zgeMWzRy-hZ_vHAQId{-&S4t+#UvqS<@SOVP2kxC;Ya~aH`hY&=-qR4CkUj`)_Hawh zn|7)0WAPl=u?T5f-i>*bQ)xi9@jNVJcnL>(XwWy-2KfT!5$@3$FW{HUY%h+iUKx zcyFH)%`U67`+J1y2G{xTUGCKEe+-b=5#{V7|E#f~eqV3Z5LFMIbR-GXpP|#xo%oxv zl%-wsolw~PGsE(~L%mfrKx*_Kj!h`cBQ*!G8>m8I?uC~d;yNiv<82NU9G#lmikslF z*z9npEI*7_`JxY@9VLX8L+OKsy>~$aJ3c@1ZA{jov3H#4m6JpA%iS6vkxfKzw9TivxRm_uwtD;e}LSlb!-13mM* zi+m?7Rvx&>2{y~74YI-+F0~$eD;lF$ISqM7pmRrO{Vp3gbC-v2L^W4b_+ zdl}YzxPy*a20axg)tVMhXC6ZEDzBprIgm3tY+r94#*DvT%?C~zCQByHqpw`vKsoVh zr(k}@M<+ZP-n5$J#*t+tXkJADt7FNaKf&?|EvYU8_Lz>ue=f>VpgwFpyFMux9_CIQ z)D$hc%CG-0u(hL>e-s6oYeGz2q->R|On(%{nR$2=DCoFW?}e)6ogS10_Ie_=N>{(h)KD}H zcgM9tJ72_?`E~_XBk}&k{9-R^69eF;^lxswe(V`qgG_+g?owRwG)xuiu#suDH_MON z09R_uBwdK4e>!Pve>nVPN5K)T=jSR}mn14pQl_39eBk!qF|m@T-v4=c2XCzc*U)K* zU;Wf%oby{8b#+Gs1TvCinT`PqHS#<>RkVbJC{~>_NOBE!NQ@XUJT{5!TRe)tk?had77oje^9?@81yU9t|&J7gaYAcTl!2RH%3Z` zY}KJn0g>C???sN5ZZcn+6{^oxjd;|C#`{o$Zg#!(|e_nHoU+ z?$VBBOtCRRe?8e8_ubxQ#VuI#04qHj7s&H~k`K7j*VHtm;f4UlFeP-r<& ze`Y4CH*8qsR1^$jH3Ntu;NVRoxB~5owJ&stU3q+vVY=b{Ioe2Ka^s^8_8Xp)trxe_ z1n!$(xehF|Jc~5hZ{6&mRZ_SyE~{LpK13v6$@M3aJ4_ZyF0pKD4Vv!P;5s+Yb7wsIWGDHuI8G8U&GO?K|Eob9f0%Tqig<`AuC3|qO~wSl+hi#kOrJ=00m)qg zAEPhWp?0Ns^85(fy$nRTWBbnQl3TPkKkEjtPK80rWDWy2R*>fpz|2b{jqtba>OmGz z;oAvMzFF^$MtA}xm_NRkf{$rT{gc%jooqWLOl)ZTeIyjXkFW(g2>zcA+lk5cJ(01LJ`?C@8LW? z1ru^ia=4wC^pOp(ww*wL5{R|(lzTe0kX%>^S-$kxE1VfUTi;yY^{WNd@5CDL@pDxE zI109~XmcuM3ZZp1&qeKo8sQFVe>>Wl0Vd+I0Hcz2f>EADLKoz{8}zqX5fRyN47MM$ zC;&7nVngyW1+Cf0@4@k;b1ebse=_1I@*QV(Bb#Ra-gl-VdY85sRih#Rk#2yM(~Y&* z*IN75%#~jmT_1Nw4Fb*a><)09I1`Emw6PE{x4BU$8v0h2K-+@xp^GbYe^%N{5ca`< z4QxzTd>=~;m6mFTBb-Kn2_8;J$XE5}%OS1unoTqHuc%>ee1cH`^obeza6(Y}L;;S= zWxaXMLfxfbd}R#OV7;6AXJFAT)Eu9p{GK#nY30*4+ub#5PeB}N5GEhT(CjC=;kX~h zY-|l$>V$svE3w?xC(m5ae@}&Tc}VpHd0a89M3xb@U?qu~W>q!2aKk)gL?&f`O#q3z zuNy?udh7V_)W%R?4f%9#_{Mq^sF3Kpy_t9+eBjMPjX8?DmK*AeApas}fk-nwwJ@8( z785E{*VG9L9~FUk*xrw_2Ouv>Uq`H$h6xUiK(`uwk%?!t3naGvf2rMULKxsh)nxKY z1-r=6SXN&7_k#-2r9%S_PuPQ83+5((k+tVrpIc#f9HyVYzRL=A$2S*f^zdE)*7^Ic8u z2&X2uyxTA$wZ2jGe@zf}a7%o?Tq2Rk8?}#+Rzb02FxRd24_1*GeNXR4=@%elMLyNi~6t0?xUh3Xlg z(kK9v;%;}&Pg00-`zd-;iLjaJNjJ3yhQ%s^5xL*azcamUe_`E#f21ZvW|v6wrabC| z()0b{RwdbW5u!J9w9=GQt3?LY{-i1~Lq7{z{-qKYgIdb8fQnC-6T%w~CS?rX3y_Ad zJ(paN2SuOKIA+b%61=_b$cZTmptVD>`y5r#YpWNXTuW`N1=NA1{HCp>#A-4TK2?MPdxc;;35-?o+le5?rzl+{u&=K@P)LhDrxfH&JjdD5MKHoUwA&wtvRT0AxnjYY|85< zBC7$mmg}xLZDGg4Kc5?#3eLJ}V&HYj#W?vOXC36|ud$aHr>AK3`C=?|_4Otpcf;lG z?XV7-f4Wv5B+pW~n2N_Gh8Ni4+T?sLCS`j2>-(&6({pV_3~^NauHD+F!MzI27KIiO z#{|PDto=;JW79Q5GYQ$syPGyKFA69PLQSHZq9RBML~#5Mcz7|^o=~TCO3#>Re?|)FlCDUh1MTI;-wn<5L#|ir{1><& zd?B80E5IUdzZ%9|yU=;mS)V4-LjQeXCs4!A0DP8<*f+476#&b}1C7&m$o7&U@w+tPM)Mu!m z^m!~-f{#h|`qL|f-iPR?S?`)pb0@RwBERf%Z26F@+<#QQk_MCU6V(pfWnKjn1P%^} zSi~`cRUR&YTD5Z3tamBWKWmPryH-+We@C!jB^j{i9K#7EjCh_qe*s4s70seio$F%E ztXk)6(Xw)E(GRzP0yR<0;0krvu=hJ+_;ijtSpmSjT^*$iyI>G$SJay`NiYOj`o*Gl zEB_WemH+lsI|+KnIwbB?au|zH6`-xaG|4F?3*XVhz}Er7HtsgwE$F=|f_=6ie^r$& zBRQe>D8swW-p6vNNe4>zZ7lzy)+@Fz%EDj=>StSgt7T$bWF)#;2V5o=W1T7EK!7Y| zB~L?-6@`|WjLntM2oQeI{{H=*ntlrI8F$B)N!qn_?NJM0pv5Cvzgm^_(re_qu9&^3 z_VM#@5@>&d=eBrFcv|m_o6Z{vO%|iK2B>%G#Nm6_#VqKC30C;@DgEyafly1Q9Ipv4QuYv#g zT^tWp3iv#G zf`mYuQG7r>`XpeCEyL^?9z}*^xGU-^<+(sNnT;C-4ml8(j@#A@L2*6i_M@b4n7F1a zewFf%MRbREYtYD>W{w=86ZTBJ3iK;t%r-7LFqOReH(}IT8S}6$Hf%h3U`fHcT~)wm zWQOnH$P*h^Y8~tYT+@|{e=?}QeoN&S{8WT#FwV7a&btM4I#9_sK{s+1%xJ*~I+k{L z2%VbeA zakUl369t;)R8sL$C)W#JHU5`e28CjCi$lyOf;}cwEF1-+e-VOSWN^E#{kyELE#j{ZE2_*qt5vd+ zf7t5f5MxI(e?qT}TMpc6=WE1H;c(!2_8nE5FsdwDqqhnKOYwLNtUC(Zca4o*-)SZ3 zDENlbtSWnE(TLY_#MWE&0Op;a3j@y_C?>WEc8gw;gw4NpxH`9Cv&aJGqZKATaPp(PRvfoD55HcX+=gJLVqq@7I`@ z-qHLrf3PZYq#p&P&)9iwM_6(Wt{hS3`XY=uVmZAPW&L_{1D+N}|(_DeLt)i?$*T zHnH4wVJuf*1(^xWDhFXJ82gGp9nO(=kNd1w)yY*4? zfBK#c=E7JnL|PEQc0(2|)py-jD|4pJ$0sJ?vS3ydRUl0y6O6a^{}59Fg$9NaDLXWG zoMc;2WL5@;R)Q1Jao*#uC@#$FbH+6?rgcR-BH76z_M)8~76Ewn(4cDo7HC*5)N)7m zVU(!Ihsn!myZU6AEJljiNvcq!A24$9e`;~ zFn6}TTYa4x%ZvEkWP6d1lEqK!@#*91UL41!f7Y zV*k`-8H|1|mLscD1;<$%{(c8^6LigW7f}44Syvg0Ai_w>5Ya;?6l@jG+e<_Sx)JA=ddYA@beNI@K!jgz((|)ff?R zVHg1%7f8CxoRE0}c_y5oK$rV!e;mn!tjDg&OyCx7UCznTV}iP*cm{pQR)vHCBRUWF zcJW2=%!+Ioq-&7mVSW@Eu90T^9jDZ;POiJ0r2T|W%mZ-c8}y%@M&O*wj&e0SsdCrA z%h&h*ILq}!)9v)$_^p47t;_~8p}abK>C~0;q2(Fsk6t2LU(O5wP1Q!xf3LD{nRrd5 z%u72ejg#TceQZ~#KFr^5*~BF0I0-GroY6~+0Rqs@YKl_O#WBVy^V-Pj(I}~zO{i#q z-zCAKBn~+_&YYA`Vh-)IVYZP7E&8wWTU@8Cw&c**QrTn0DeMAMNXoDfD+J)b4x|da zy84;}pp+)eRdlmm-iDWTe=vGY3M^rJd(4RNYir^XD9cV$CW;EB7u;OnZOcivR>FVG zHH7_R^|^rS_Ak->tEQG;hiMQ`j_Qbk%@e>@3I#$Y^WYS{*bDWfuWiaLav`Zc$Q&VN znWl(%<8`9qar)scLr>uyE9$d?6^l6ia9jk=aTCP6+6s{YQwzwGf3#7{DDO~}rUu{@ zMmPlFLaoK{&utAFuGGPFCx<$P#}rwnDT20ghHmTWr*-rTNHoKac;&g(9qQj_S* z_xU-qO=;VTe83rNml-#aW^&CjN!Qh^as1Tf`8@UE6w+3L+5^twCqmxK>N9pCz=%70 zksiTYo=PNS@DAI*e@a3OJlsSKaOF-J?X|7|B+kJ|u0Oc+n=KCJZnqoWY_KC6ERMY9 zqm8z{Q~JjmY(W@k%KGmc{r8AiKj~_uU3UQDJB}exi`{@>at|{+3;s-tZJwmlwWO!0 z?E1J-DIeFk*LY)$f+IkXN-2hOV*vIEe~S%QV!T#kDzgsZ2K8=4SD;wGO!J3$fj0nC82vX~oNFe)(J@Kg*D(d~DSN?%BTxXgM~Y3;w`%^RHh zzKuMZnw6TspzZ91aqO(AeBbaAVm$KDn7_HG0Nr7iGh;KF zlQ^mfczWVKf9>(h$Ssz7pBW7k2s~Hv4K92&dDN{FyKR_)irlZh0O0rj`7_L7hTTQV0afHPxgsiaHlov%UeT4;=Q1Y#|uQ~nCB>Oy} zvu6AC9s1Z3{Gp6<6gbW86lcj9UWr}37`OvOszJz5e_MR440qYofn5n;!_SXj7vDk{ z;d*g6Q<9#oFZ4=F{``(DCz;1`>FXSItifZqr{RDzdH)oPGG)hlLJuq@(YCKZ`cB!e2q%Y4z zrg04ke^>%N|6L%NHE!Ja;$aML^K&23P2|c=pgIsAK!RVz!2o@tY5d8$W6hLcj< zsEczY7wg5K8gBG}-6HT{d*B~l9?U?3+?+;5nx$q(&f_D`2GwgcbZRryOJbgQe4gNC zm)QW*JHUQ|()n>pX)WKFg)f2AA&Mpt*8e(Qe}*|wG4XbN&Vmq|S%0Xj8c^t*Q=)=! z>v``Xa}!@P$1Qct`Q#dV?bVd^u#aC*RG2FC(oC?k=xPw~rXca&kYwt$Agpg>nM=-f zfArj=>*7=ex8n~q8Am{Pxz(iE85gq!Zd_nithOe!<66AsGz?F;E!(vQyz{eJ>MUf3 zKFQ?~>I68tPY960G19|RkGcTFXvxqIhnJ1K{Z8Q(Il%HByKl^NQQ`>b!NOc^2BblI zUpQS)@%r!h9WC=GF!^A5+xv81;?6~ge-$!DRE>P0mk0F}I&GwYn>!ndC9$|<_^-Ey z*lsVvI15NhCxP*zhy!mwvB?|8_gc(XeJRhQ6C)m0*JLnf_F$pEQA+u*DV3D~gpNzz z950{LkewR906%l0{EcHF$~y9g2VFYHH{8Ny+w9r0B0z^)fNH7I(agFuWpVe;f3)oi zRD>ZarfV>ey+}Tkom6}^W>F7dO_^n_9$QuY*LAWU*OVk+DY@y@E%O40pC2) zO?twyOyWfn!Je%+zKie`L#>c&UVNu>(<$lVCy9L(s(NaMRUS|tqF;eId$#U=FC{{I zgWna4k!FH*2|%C|t)y*RQ1K2tf1;vO37qKy;E>Qy;stxgC@932A4;T2x1h1Re*+1H z+Psar#$wt;XDI{#tjowVLg->CmXQe<+RN@Bci(#eEVm^`Rj*B`VX+4asJG?Vx!26> zMImWoQ5aLWmh=1K36qIyq4UnqKim5u8A}#w`uzI`upCC*u&;@&l!MqZe?&U7IM6v> zjmVCo4OA+D<(t*1T`CSVqzMG|_fDKVT~UCn>D31?A&{I|1Mm4LKUtN3N)aZVeX;@` zvwH~2%!tp*{l|1oEx?_A&S7)1eL&=l=p(ovNnBn!CjsS=o`g|f(@{JZRW;IxSbzq0 zgqz~&*!Az>(`UUQFju_+e?}7qBB%f+KbLSJI2EaeL2JFWs;Y*m^#JyH>TP9mXC0pTHQ(n1BZC{Iu)WMz$>AXvj0v1bNCAynbaU=tI&F$yeV`Gr|E+Z+}rp=G`)fULe%qE zUBINPSW~=6D;h$)3l9lH^qwEB>Eo-+LH46t$UPlx(n$I(b-s+iVNHl8df$gtVWEU? zkN>gEJcmaf5EvS9hS05Z`YuVevDgQoJ&id#kx%o)0k}+eJ!;?^LsH(`t+hsPdB-pG zvqWC3O3a^QIymtFf6$#>kW&r+EUvk!M}95SUEt-_V8IywgYgIk?v_~r3OuJvMWyU3 zAA*Nxdi@^*n$tFh`bGsS*@}eI^vVp%_XQC9347H|eIrLIhc|kc^|Y-+$sPt9TzGuF zKwkIa|0@+$#AL;_`mk4F9@NWP zSBwNcq@b{Pbq;~?*Z}4p6j-f2Q4#Y=MY#7G&n9e4e@kS`+4V7B;U=fAZ%adop*2Jz zC7++X6h<(jE4yv$*tUx9ko0*d-NtnZ(#MR~XC{)f-Ev;|IU3F3{G=ivr@!*{XlLQe zcEdkUGhmoK<`f$e$*xB9m*(MPjb)e6ND;k^_42j<(xAEeEG`AJFUl@yvKRWe`?tyv z2WXicfAD8;(G(F|3n=l=;}e07VAv!VQ<2^6gwiOXME!xNlKImOz>9LP=};TBOB$jloy10x6-CDXTk*cwi@K zf2I3C_0$^JWy5uHTSA5NEadt@N7$G)E_ zZB8STqX`WBf~h(wSY=tqkkb_O?h_FRcgW655o-le+l-DZf{&`aVI7lF1Tp?i+?NZC zM6Il@{O6`&(two}ER6Q{eFlztfH+KUf0Kx=Hm8ta41WxqQ8qihX*OR}y+%WBCkiHa z9_7^($5T*r#x}Jz4_7$@8~U&@|9OmDBn_di)vOuS^=Kh(?UGY$bE^vyQ-ybYudF6Z~%e=Tx2^ohyrE?zpH-p5(0eCx_wi^Cjagbwu&Avizw zDi>9ynk^Ur7*YFWZs5f;)4$=?_+49W#otG%F~|MfW} zuJ3z0AA*#@^dR?YByfi_*y%Xa*B>tvM*Vw6?7U%YnRl_eUqWnZSd?WtYLrXs zMle%me4DyJX)Uq-)weXRBZ#(B3WUbbL*roMaF0D!eF8z(pXp|#e^SN9_E3m7qyHWd zi%ui(wx|GweuKzsx*ZheP?)|!Dyw?)Ie$%pi_M{}AB>AAH@5;8CIdQUG0O+N0!Vod zU`xfTe=`^r8ScoEo-LxdWMVJJgN29@=T61Zx<}n8dLl|?ooD7OKp@WEbyeH>uD~_0 z@L|sNa5M71q;0Arf6j>5?P7bYQu99C?k8y`h{FQ=vRyu9x@oY4E8d+0_P%ox84te) zEwU30(Tyc>C;Aea7q_Rd{DHyK26JdVPwP$W`WHblTr6_EQ&nvThkg2lBiOffL50C^ z_1lrD1@pV;QpIlOSo$taoD*R#C1KF^3_){x(u`!SD!>+Pf1rw|5KzwrEZJXr!>>dg z+DtsHm0nX)0q4xIVoH!K*l=adnjxNW$@x^ZN>fX_+=DAOs3u}nS4j43Taggj(Y;{f zJJ?Rp_Ryc0x=NWgCk_R_Pb+EkHbXs$-^sY8-X;kwQ8o#qn;Z~F4t9AdVn|R4k0`nF zUFzm9#C(vpe?GNP;S>zpY&0i%2+L0@vw~bc`yf|?-6w#EM!Irn)``bwGw|fHpV)`m zNO?nAkr#x-WJxXG!@-Gg&_1U(w1Y5g@i!lVJGaQ3C*P(-2+_>TrG}`F=OFE)tc){_ z_+tYaM!o8J4vzLlRbKO#L|qq^jj$#mUf3q1DIEP%e*~Dhu@lJK@HxalV#tk~!2{X&C4cJbeX2Hm8$42LJORtaVeVNeo#3(yg{ZyE&nPcw^ zhEzMUBmmrXfAmJ1|3C(x*!PNv>xrrZH zb0~fv75vrBFcP<3#5%9&pRlH)d*l_By;SV%hs?`j500IZ8Yi4zy7Xr9I!4nQV6DcP z8ZBfQ?Qf#+iF>2q9f=cR65#r`JX``{ljJ7$LF^TB5-7 z#U$A1tu4(1I7usF!c&1GtOeEF}3f z0W?edLjJ1%9^Xu~_gCs5XPpa?!tRTzvOUj0z|jtAnOnl1E+49V%1J*tMsPyKe})vc z=7EW&7%fiFiWDM#TltTOow@4;bW-bU!2u^=>fD%JH@Q6p>S4Ri6 zAs(#P{^ReiYQNQVULzm%beGNzgh2Xpc5_*oxrSwg5=E+wFX4ji!V9N7CrbPy6YA+O z`$Dh-5WQ67+Y@2Uk8%*fUQ?OKe}a$|d_ObB)9SY*>sODQqn;~jN@lebN{T@bLR3jb zkTX9*6@y+g$XwCTUqHzKTY>VaVVj|m0e0Gz2d_x@ebj)fA?X2+uH0@j>O=_v+{<~V zTAJ$%FG9zJoaX52xk! zLP7cMjiGLzd~I9((qa{=n)h5^tsd#2S! z%k2dtP-lUHuZ3&P7-ArY)Ss|FGx{Xjrt9SyQigOx~1gf=Wx>8@lAs*>PCVo7kpQ12B71 z5CP0PS-zVA>cO$c-RO>YWZd*B8xaG~<42PhiCijz zNv(Y21LLt(bNd4NAy6nu)NY{AUfQ&`oMj59ldZ*F=Sl0zf6q{$+C)QeMpzSW9X(?? zqeVfkP8vK|RSp9tG9nvZfhqwW6N<2zz$Un4qeislf?WA7QI+G23_)+yJ3kKdUi`k5!jKgZe;>938hTD1q4vmZUgDu%U00#k z=B#0Z_>d*V?MhB~7D1G2G&y8{*^7}D8x_)ki~~N@lIF_10qyXUCb9pTzuzRWAZ9ri z9}$NsSS*Og@OW9O9P5_mPwB5Oz<7pAzRNbUe&w(!eUogq&Jg$ijibpwtqM|3e(_^y z&2!#Af0{R)#iYsVDx@L>IPMJzKE!bU+F`5_2Q`Eg#5)G~#>$);L(-Z?_vGHJ=pvFzp^f4#RA=*BlPbchq^K_~QkGe2Ru^G2WS z-?(b2wX@0c?SQB%(ZW-JfMx-UFFzj&oeGKcII?(oI}~~G>$`RjjS-`k5bfkawp0X1 zUT2A-s^Ew&KfeM`(6ot6*wwiWQrN ze>o?8vF4rfjumYz#JuRt5MV%iH7K=N9Sl({viAHDhp7kA40#n`3-hOr%;gfE^Z6aD~-=WC9vCpj*dT3f02x1 zgW&BbWQMUoZAq4s6`hVd*4E;irlh+YeBt}*vLkZB?^fvRD`u(ik5XlxSc%vYdwVm- z2C6z7^*6kD?&Vlkb+1NWl8xwY8UN|;S2qX@kK8nYe7L8?4vCCRkrzY1N?htZun4aS z6if4jm~*ymw+$-q<(QDfS?VChe=Tz}F^B5HaVJ$PCt6QXBzb!DPN+C)zabo&clF=W6r3UzR1oWzqE0ylg?I2sg;?62l-Bm2$MT;4V ziFk8*6N=+x3VYqt6gukgzKUVF>NHf1X&6i<%2bd%ds-OpNbjH973}IMe~hN$org!F zmYRbfw^>i@L)h8(izoYu1rJR#i|}LVbUIl$=HxW~SD^@LV?D{slg7ZL|Ic(t)L8Qp zav4gGOMWUsP1zNq7VUAuN ze!Qhe_^A=riANv6g!h}vfkJGnH`zzU8?cmbPx!=(tL&=ImO8=%R2CH_;nHk6I$EvK zaQd3=GRhuMb@|Pe{8mwNVr>odQ9HH z;h-i_tmUw!6}@>WOLC1-ASF>*^9?IA%!hQde3EcF&bGrQJA;v>dJT%d`i!8c0=EG z27SK_D^{I)r@cKkf5uLRuMyDPy4dIi^$>oe+jtpIWfsgIM!`m)_V@;hr%`vu3WI7Y}xZlk`E|H>Ne`ZhAN2oFQ`&^y+h$Y9p zTc5nZuw1|$M(q_X04EzJ17}jF2o&Hu!pnaQkQIwN0@be@{9Y1@ zIDtnO^u2#Vf3lz3)O$D7AzD;}$M#d9Xru_s_H&VotAIq=1D2q0+L`4Rww&US7k2R* z+w9e^74z^qCE`$*%a~u-gaxs0Ei^1Ho4~tU!zA4!yLp7Nd&S=i?yGuL888c!Vs?%n z1k^6wT&ixuF#i~$NDC+}72+FXo?h8^`{d;XXG|kze_(LmyL#n28gUfVt~ip}p0q@V zQoL{R@4(ub>m;?M8=Gc|O?nm1pf8mR1@D><@TX-^rl7KuNP9{SwRSu-xx}?)qY7H} zM3dAbN`WgI3U^w2%`$R}lw&BL!HmZ-%eGrl2n=95S$YwQAGzA*#oI2Y4mMdO#`&yE zRXSX_f9&!f`#hlvr2w;%5GnwmtDuh`^>9o?&Vt}U+R|irjWP2B8gW2Sztmy`Rhpvq zOU9e@?{o3k9hNhQX1vLuknh{6(kC-Q{MZN{4nIdN1Qve6L}1(c9f6dSs>U3OVzMr; zuN=yGvb%hyg2;h`TurAu{ zpDaz@UGNw7?_Lo|+k>LQD)%rN4!<#+;OF(ZOY;=0)F-5@t`$NP9G)d9GrE>7b1C|y zZd|SIa4!jld$Bc(j(53jJ;1mLJ9Ha5((q|Hdxi%dt2$m*|T~3v!7wCq_JN4ga zbU`ZFmpYnBE|~ZyzK-KY$sMa7#_Z} z3J;OmgoMUozvHm-ySX1CZ5_-JyICXc^SpN;zaCo}AJ$?D<93Ln+GxnF&L+T02Z0p` zO_9}no=5;zw!Nad04N4|%_7eY#Zl5(4QmgHnujq`OvPmSkWakPL{WGfh&1Cyf5@vr z@x8UjA7TLq*VSQTM#wlriFML>E<=vnUO-im@JFg#tE@Ib3Oqt*TZ1 zQ^od)&4h_S#r4r>SyDvM%(g&eWse0=5~Q(Bv!xy3G41&W2g1d;k~c@t^_m)K<=f3Z zp+|;HZkcN9>VT(~ts#vn%kY7$;x6NWmLSF%qwDbO!h~4FrIpwDn?$Qce*iu3n+W>| zk{fyTaNH6LSkT|)#xxoukGJ*h_l zUGhvDG=eJ(R>n5wWNmkJe@qVw-qM9c*U!;wX&mLiUAIC@9=P6{kzhy?22|k(k-zvL zHSZsD>}tXy*jBIP(mEq%KS>q-%epn8VgR%RuJ&yb6@Ph;MFzfqFp68hXF>i_OlOZp zV6)lDchlQ!H6UsRywA%Xk5fk>?I_jNf}1B|DF_7~`mTwZWc>b&f2-P`Z&n0otY81- z9$%%R8XKzo>t4#!U-jiKlO1OAJ0drMC@Zyd@4LAPp0LJI0gjZCKuROX#9&3zALD9cq0UBOG>xZL*1d&j_j^wT zThddO+Rs~$J4Y+1@Hge;|E^BnE#N?VBlg2lgF$p9O9I5xf7oe{;^ign^U2hXHcTgM z2Z{S*!I?Tit%YsbU6uu4h+11Xl1L}nTG$!$Z5C}?2ihbdacZk$@Gf@YtP$*zEAb8p zY=v+sk4Pw@`j_`~XxDDM9LqB}!dy?0tR9#>T$F&%-L=N-nDmEKEMnP)2UJS^b0G;T z`ci#WxmY)Kf9kkFv6i1~0d?YuQMAR;r-pd-Au9@jFAisVOWDU2>CtwK3?HzR(30Ab z57ed6pF$((#!oK}4t16T{|PjUabW2yA+JC(3uG>0lAy%IsE}5TLgrqO2wxI%iMo$4 zrM2uFx?aIXAvLNd@G7nqJE45;3D4aB;KpYncM?>|f9;V$|8O6a!>7!g4R>dK+vTRz zFiI-^vm?gmT%lk`8S?HS<3{*^z94+B7FRjgx323Khh{f&T*HD5PM5UtGv%N?1`F|-W%S~L>l!%M4AeiWn6(2 ziWd6df27>+^FPH}>?fd=#h;{x0Vra#t^ZfULf(oKnm$VZOzKgYQDwS zViZ+OanG;a1<+FvRn zL8aYEoj`fZ1T~&Pi%`2&b1gIl-X|rPfw|3{NEh9vWoPf@zASfBt zJXdb?7FGWqv*T@x1YzmL&6af!I%un`ok6rQLAp%K5@w@>-ywxEZp51j|1~Uu#@x0b zf1q#NZ;P-2Kzx0*uIPk7gyF^g*C<;wMRbK5}OAVg0&6E75^AuHb;g#uFUPMQg zfLK)Ta1ZBiqT*iye16pOW&cOlwSa1Ie|MUMMY~j%h+cJ<^rDA@ZMz%(a?ES+#D z^)2z1(g(TnVoZHq7CZFms#3L|&N9niY^!IkxNp1|kc;tpo9=x#VQAc=9@`-UbcGin z@#6j~O1axSihVqAb@TAR>H26z7#dqxXeR#j6|;N5nwGahi5im_u_62f9(&yPc9pe) z^RIHowHy*bxsXoxLYkjGG3&&Tf7axCu~vPrdKg3N=gmQtIw6rOSsV_ku{VRPEhf&s z^vGA{WH1&r5r%Cn$WN({5ZXqB+Pw@y+_lk2gg?{(k$vk0XUEX1Yf9BnZn|Z69 zJqvCFz!~$E1j%!7lt{tsLbG;lnp4QXJL!K9rv)qlOSGF~&)U)OJJ;v?dxP#x!ILDQ zY=(Q>JA1;W?kooXA8oxr0g9L6|6JHw8|GC;0X<$?Bxv8_Y*e?K3bCo3rWj zjs+~?8UFOxv-hVu0G(knuWXoL8)X?**03#)MuepOfP*Y@e+yEdhsZeor1&5(AM^fd z6H`($*^du`g8(`E9usZL>G0GvIEk8niLJzdzZ&x2qTdTAp`rfz+Rm06D?U*TxPYIt z-kr&SuaE3I+fuXC#&v+sJZ$EGNePl2X#h$KGPBh)lp*L>lG1MQ@+$68hh>QTbk8iVyR#Qc_&5v($RDng2S#?5% z*{Taj(AH}6Ra=~v89=#Oy4J@lsxvy0j(%9Z_fZZKQ=LLB{C{@!UAT;G6)L)S5Zx;F z%|^un0$eQ@6IG#P{@Um-K>N3Dc6O)WX}4XdYPRa4hHTQpsvS90`yN@B;zmOv zYC@e!c+9MSNqwj{64(nBB^3I69dgZ}tV-a6=z*YIGk=tr2Tu39c2@~Qy2M|7P6<~> zUk(wXnKWqi7x+Lh4{Ra!>i3@hxUrMZ=_r4+#JP#$-W1;jaQRkPq1$tzY-&2x`?;U~ z$0R%FrlQWnDo-RNpY?x7j70SMtNV5Kl*g-=%aMW>q9)8oc$RewdK=lN21m+-Nh0hM z#9!WcUw_vNSKZHiWp==Oofn7=;;TI}2Fovrn4ITGTh_E`0Fca?5iUMfu(1Dgd;H@t z-u;ljYZ!`kHQHn8dlz2tEZYUQQMIHo2m2M19KNsHSXni;|7FMy`TidKShx4Vq& zJ$xkBdxbH*hP6Hhw1q^I1Cy*KE`Grp1C-i9eSeKeWVj_bR@0J&f!aV7D|9>^x#R=>>wYV#ruxu31fT7{zqQq#R&2W zP>ib{exLj&^@1E>E-*;h5SjI8-(9dxj(_Sf+B_@jgMkTk+w0I})X)inD;QJV(Ogdw z+c%nhTH!*@i*_{%d41fiP@2)irK&lIs;BXZfHh5#xg)U@zgFS)9^T=uEeGn}Nws{G zK|=Mp6^S`&_`(t5dmUGPVo_oRs8N3x#__7d;wyfwl*1A18xA8r%XlRQVlg^zJ%5Dz zif+94F3fP7Y->2OXY!bM8>EE-mI?W1i*|9CMdVQ*-;m6}LP~O0X3p}c)?dHS1D}kN z@i|45y1#u+cdVJ3^X!mE+pPcaoSJ~$ryXybvWl%N;huE}L0~ZZU)n1W#t`R;Q#5`y zHK#$wD2-5ieLd6Ra=4Khwo4R(9edmd#=g8b#S5X z=mmf`t8+qMCrp)s0>agXx^8(FAz*|w42VzLokwqQ&P+QSdzxWSY!M6&j-U!L$q7R5 z?5-IHdC8-8g^^kbt6?w+0F5izjl_o886VpIomeK#7K!gMU}V=265w0g^?%kSEs4k+c3Xs@S8NtPM$y(cTsjsTuW(Y4;+WYQh>9}ct^ znoFh@)G?%mlvgAvURy1Msr1cH4ZToa!x$EZ@@$>8OnsD3Q)ehjCBc7N9~-y8S&K8} z#66sinOoXd#()sk_g>UW1%Gven56@=gPJDzE;L9mTQ)^0GQw@fGpn0_Bndnu0_Xy~ z=nReGO6JGWWpWoM6(+vCTm8P8(XyPX&0JvMu)M(IU`n%HE5$5sjlN5tIXBpE;>KgK z?vJ@ssAI-zwW3IW)qr$yO21QL1X5mhho}=xYu-^QFbelDZ!X}`<9{u|R6$;@PD6>* zsG!Pk%=rHa*s=pHqi9pT3vLv%f5ol`CcQc zL~3itujF8qJ5abR@qac_PpJcP2Bve+b+e z8DVx60_kGCK)Xpq+?@n#xzB2|Niu7Lc1pCX4aCsNgz{cjU}a3(y6|BWD``Qh z(|+0w>9-cPvw#1yGq;~iO3m-c!;eA5KMkus%4qVi;?eRBStHZ5r9z$W2P-&;Vj6s`wwh5r(aE8-HlGZs;#AF!ye?0z%f}OHT

v2g5k+`fhHh85gmTwbed9-e- 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

j*6Uyxd7X~ZHeZ43%<8E#CK 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 { - fn commit(&self, msg: &[H::Symbol]) -> Committed<...>; - fn open(&self, td: &Committed<...>, indices: &[usize]) - -> OpeningProof<...>; - fn check(&self, root: &H::Digest, - opening: &Opening, - proof: &OpeningProof<...>) -> bool; -} +\vspace{0.3em} +\textbf{1. Per-codeword $\to$ one tree.} PESAT commits $l_1$ fresh codewords. Leaves are the column-tuples $(c_0[i], \dots, c_{l_1-1}[i])$, hashed as \texttt{Vec}. One root authenticates all codewords; opening returns all $m$ values per index in one call. -// Multi-vector variant: one root over m interleaved codewords, -// one OpeningProof for arbitrary-many indices. WARP uses this. -pub struct MultiVectorMerkleCommitment { ... } -\end{lstlisting} +\vspace{0.3em} +\textbf{2. Per-query $\to$ one proof.} Old: $t$ queries meant $t$ separate \texttt{Path}, each carrying its own $\log n$ siblings. New: a single \texttt{OpeningProof} covering all $t$ positions, with shared upper-layer siblings sent once (path-pruned per book §20: union of copaths minus union of paths). \vspace{0.4em} +\textbf{Concretely}, $n = 2^{15}$, $t = 100$: \begin{itemize}\small - \item Single multi-opening proof per commitment (fewer auth paths than per-index). - \item Backend-agnostic: \texttt{Blake3FieldHasher}, Poseidon hasher, hybrid stacks. - \item WARP migrated onto it: \texttt{Path} $\to$ \texttt{OpeningProof>}; \texttt{MerkleTree} $\to$ \texttt{MultiVectorCommitted}; \texttt{H: MerkleHasher} replaces \texttt{MT: Config} across phases. 33/33 tests still green. + \item Old: $100 \times 15 = 1500$ sibling hashes. Many duplicated near the root. + \item New: $\approx 100 \times \log(n/t) \approx 730$ hashes. Savings grow with $t$. \end{itemize} + +\vspace{0.3em} +\textbf{In WARP types:} \texttt{MerkleTree} $\to$ \texttt{MultiVectorCommitted}; \texttt{Path} $\to$ \texttt{OpeningProof>}; \texttt{H: MerkleHasher>} replaces \texttt{MT: Config} across all phases. Same soundness, less data on the wire, fewer hash invocations on both sides. \end{frame} % -------------------------------------------------------------------- diff --git a/src/crypto/merkle/mod.rs b/src/crypto/merkle/mod.rs index fae02c1..e7e0a7f 100644 --- a/src/crypto/merkle/mod.rs +++ b/src/crypto/merkle/mod.rs @@ -9,7 +9,7 @@ use ark_codes::traits::LinearCode; use ark_ff::Field; use ark_mt::{ - layer_stack::LayerGroup, multi_vector::MultiVectorMerkleCommitment, shape::PerfectBinary, + hash_region::HashRegion, multi_vector::MultiVectorMerkleCommitment, shape::PerfectBinary, MerkleHasher, }; @@ -24,7 +24,7 @@ pub type WarpCommitted = ark_mt::multi_vector::MultiVectorCommitted = ark_mt::multi_vector::MultiVectorOpening; /// Authentication paths for the opened indices. -pub type WarpProof = ark_mt::OpeningProof>; +pub type WarpProof = ark_mt::OpeningProof>; /// Build a `WarpScheme` for a given hasher and codeword length. pub fn warp_scheme(hasher: H, code_len: usize) -> WarpScheme @@ -37,9 +37,6 @@ where /// Encode `witnesses` into codewords. Returns the codewords (one per /// witness). Each codeword has length `code.code_len()`. -pub fn encode_codewords>( - code: &C, - witnesses: &[Vec], -) -> Vec> { +pub fn encode_codewords>(code: &C, witnesses: &[Vec]) -> Vec> { witnesses.iter().map(|w| code.encode(w)).collect() } diff --git a/src/lib.rs b/src/lib.rs index 3bd8d4f..150181a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,10 +144,7 @@ where td: acc_tds, w: acc_ws, } = acc_witness; - let acc_fs: Vec> = acc_tds - .iter() - .map(|td| td.codewords()[0].clone()) - .collect(); + let acc_fs: Vec> = acc_tds.iter().map(|td| td.codewords()[0].clone()).collect(); // Phase 2: PESAT let pesat_phase = Pesat:: { @@ -482,10 +479,9 @@ where // Re-commit and check root. let scheme = warp_scheme::(self.params.hasher.clone(), self.params.code.code_len()); - let recomputed = scheme.commit(&[computed_f.clone()]); + let recomputed = scheme.commit(std::slice::from_ref(&computed_f)); (acc_instance.rt[0] == *recomputed.root()).ok_or_err(DeciderError::MerkleRoot)?; - (acc_witness.td[0].root() == recomputed.root()) - .ok_or_err(DeciderError::MerkleTrapDoor)?; + (acc_witness.td[0].root() == recomputed.root()).ok_or_err(DeciderError::MerkleTrapDoor)?; // MLE evaluation check. let f_hat = DenseMultilinearExtension::from_evaluations_slice( diff --git a/src/protocol/phases/proximity.rs b/src/protocol/phases/proximity.rs index 34b3351..31a366f 100644 --- a/src/protocol/phases/proximity.rs +++ b/src/protocol/phases/proximity.rs @@ -8,8 +8,7 @@ //! IOR signature //! ------------- //! - `Statement` — `(queries, l2, t, n)`. `n` is the codeword length; -//! the verifier needs it to construct the `WarpScheme` -//! used for `check`. +//! the verifier needs it to construct the `WarpScheme` used for `check`. //! - `Witness` — `()` //! - `ProverInputs` — fresh `WarpCommitted` + l2 accumulated `WarpCommitted`s //! - `VerifierInputs` — fresh root + l2 acc roots + opening proofs + answers @@ -143,11 +142,13 @@ where // (acc_codewords ++ fresh_codewords). let shift_query_answers = { let _s = tracing::info_span!("proximity.shift_queries").entered(); - let total_codewords = - inputs.acc_td.iter().map(|td| td.num_codewords()).sum::() - + inputs.td_0.num_codewords(); - let mut answers = - vec![vec![F::default(); total_codewords]; leaf_positions.len()]; + let total_codewords = inputs + .acc_td + .iter() + .map(|td| td.num_codewords()) + .sum::() + + inputs.td_0.num_codewords(); + let mut answers = vec![vec![F::default(); total_codewords]; leaf_positions.len()]; for (qi, idx) in leaf_positions.iter().enumerate() { let mut col = 0usize; for td in inputs.acc_td.iter() { diff --git a/tests/verifier_negative.rs b/tests/verifier_negative.rs index 302a300..b61c80e 100644 --- a/tests/verifier_negative.rs +++ b/tests/verifier_negative.rs @@ -154,12 +154,8 @@ fn make_fixture() -> Fixture { // Phase 2: the "real" prove with l2 > 0 accumulated instances. let warp_cfg2 = WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); - let warp = WARP::, _, H>::new( - warp_cfg2, - code, - r1cs.clone(), - Blake3FieldHasher::::new(), - ); + let warp = + WARP::, _, H>::new(warp_cfg2, code, r1cs.clone(), Blake3FieldHasher::::new()); let ds = spongefish::domain_separator!("test::warp::negative"); let mut ps = ds.without_session().instance(&0u32).std_prover(); @@ -176,10 +172,7 @@ fn make_fixture() -> Fixture { beta: (taus, xs), eta: etas, }, - AccumulatorWitness { - td: tds, - w: ws, - }, + AccumulatorWitness { td: tds, w: ws }, ) .unwrap(); diff --git a/texput.log b/texput.log deleted file mode 100644 index 438b14a..0000000 --- a/texput.log +++ /dev/null @@ -1,20 +0,0 @@ -This is LuaHBTeX, Version 1.16.0 (TeX Live 2023) (format=lualatex 2024.1.16) 3 MAY 2026 20:03 - restricted system commands enabled. -**iors.tex - -! Emergency stop. -<*> iors.tex - -*** (job aborted, file error in nonstop mode) - - - -Here is how much of LuaTeX's memory you used: - 5 strings out of 478285 - 100000,1977958 words of node,token memory allocated 270 words of node memory still in use: - 1 hlist, 39 glue_spec nodes - avail lists: 2:12,3:3,4:1,5:1 - 20331 multiletter control sequences out of 65536+600000 - 14 fonts using 591679 bytes - 0i,0n,0p,0b,6s stack positions out of 10000i,1000n,20000p,200000b,200000s -! ==> Fatal error occurred, no output PDF file produced! From e12062715efa7afd8db3bce13fe9d3d2915b032b Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Mon, 4 May 2026 21:59:05 +0200 Subject: [PATCH 26/68] ci: load ark-vc deploy key + rewrite HTTPS to SSH --- .github/workflows/rust.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 491d77a..d4d6374 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -23,6 +23,14 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Load ark-vc deploy key + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.ARK_VC_DEPLOY_KEY }} + + - name: Rewrite ark-vc HTTPS URL to SSH + run: git config --global url."ssh://git@github.com/arkworks-rs/ark-vc.git".insteadOf "https://github.com/arkworks-rs/ark-vc.git" + - name: Install stable toolchain with clippy uses: actions-rs/toolchain@v1 with: @@ -47,6 +55,14 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Load ark-vc deploy key + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.ARK_VC_DEPLOY_KEY }} + + - name: Rewrite ark-vc HTTPS URL to SSH + run: git config --global url."ssh://git@github.com/arkworks-rs/ark-vc.git".insteadOf "https://github.com/arkworks-rs/ark-vc.git" + - name: Run tests run: cargo test --verbose @@ -55,5 +71,13 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Load ark-vc deploy key + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.ARK_VC_DEPLOY_KEY }} + + - name: Rewrite ark-vc HTTPS URL to SSH + run: git config --global url."ssh://git@github.com/arkworks-rs/ark-vc.git".insteadOf "https://github.com/arkworks-rs/ark-vc.git" + - name: Run tests run: cargo test --verbose --no-default-features From 166c29cac08a31e41eee3d1b1fefb01609d21e66 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 5 May 2026 21:35:17 +0200 Subject: [PATCH 27/68] christian comments --- docs/paper-mods/slides/iors.pdf | Bin 311229 -> 317810 bytes docs/paper-mods/slides/iors.tex | 35 ++++++ src/lib.rs | 126 ++++++++++++++------ src/protocol/phases/batching.rs | 54 ++++++--- src/protocol/phases/mod.rs | 140 +++++++++++++++++----- src/protocol/phases/ood.rs | 51 +++++++-- src/protocol/phases/oracle_handle.rs | 121 +++++++++++++++++++ src/protocol/phases/pesat.rs | 54 ++++++--- src/protocol/phases/proximity.rs | 131 ++++++++++----------- src/protocol/phases/twin_constraint.rs | 153 +++++++++++++++---------- 10 files changed, 644 insertions(+), 221 deletions(-) create mode 100644 src/protocol/phases/oracle_handle.rs diff --git a/docs/paper-mods/slides/iors.pdf b/docs/paper-mods/slides/iors.pdf index 811f5f33680333dc2c86fe80f1360a5b1430d404..c111f5de49befc4ac36578aa6e3ba0328f1aacf1 100644 GIT binary patch delta 98541 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∋gtUw5) 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�BZ5^uoTO^`k7M!f)NU}a`~odlw! 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 BD=`27 diff --git a/docs/paper-mods/slides/iors.tex b/docs/paper-mods/slides/iors.tex index c0d2503..c205106 100644 --- a/docs/paper-mods/slides/iors.tex +++ b/docs/paper-mods/slides/iors.tex @@ -319,6 +319,41 @@ \textbf{Document the pattern.} Short ``how to design a Warp-style protocol'' guide \,---\, narrative, not a trait. The actual lever for practitioner ergonomics. \end{frame} +% -------------------------------------------------------------------- +\begin{frame}{IOR trait revisions \,---\, signature corrections (1 of 2)} +PR \#22 review surfaced four corrections. The first two are signature tweaks that document contracts already true in code: + +\vspace{0.5em} +\textbf{1. \texttt{Witness} / \texttt{ProverInputs} $\to$ \texttt{\&}.}\quad +Today: by-value. Empirically read-only across WARP / WHIR / STIR / FRI / sumcheck — no protocol in this family consumes its witness at the IOR level. Switching to \texttt{\&} documents the contract; the consume-the-witness escape hatch we kept open isn't used and isn't likely to be. + +\vspace{0.6em} +\textbf{2. \texttt{ProverOutputs} $\to$ \texttt{ProofString} + \texttt{ReducedWitness}.}\quad +Every nonempty \texttt{ProverOutputs} we have is unambiguously one role: PESAT / TwinConstraint / Batching are pure \texttt{ReducedWitness}; Proximity is pure \texttt{ProofString}. WHIR's final-round reveal forces the split structurally — the leftover folded polynomial is a reduced witness *sent to the verifier in the clear*, so the role boundary can't be ``private vs. public.'' +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}[fragile]{IOR trait revisions \,---\, structural additions (2 of 2)} +\textbf{3. Verifier-side oracles $\to$ handle traits.}\quad +Today's \texttt{ProximityVerifierInputs \{ rt\_0, l2\_roots, auth\_0, auth\_j, shift\_query\_answers \}} bakes the BCS instantiation into the IOR-level type. Slide 4 says ``by handle''; the code says ``by precomputed table.'' +\begin{lstlisting} +trait IndexedOracle
{ fn query(&self, i: usize) -> Option; } +trait EvalOracle { fn eval(&self, x: &[F]) -> Option; } +\end{lstlisting} +Two impls per trait: trusted (prover, backed by \texttt{\&[F]}) and BCS-checked (verifier, opening validated inside \texttt{query}). Phases become BCS-agnostic; iBCS / ARG composition slots in by providing a different impl. + +\vspace{0.4em} +\textbf{4. \texttt{reduce\_statement} + inner-method split.}\quad +Today the prover and verifier each compute \texttt{ReducedStatement} via parallel-but-different code (TwinConstraint: prover pulls $\zeta_0/\beta_\tau$ from sumcheck CC state; verifier recomputes via \texttt{scale\_and\_sum}). One side gets patched, the other doesn't, drift ships. +\begin{lstlisting} +fn reduce_statement(&self, stmt: &Stmt, in: &ReductionInputs) -> Reduced; +fn prove_inner(...) -> (ReductionInputs, ProofString, ReducedWitness); +fn verify_inner(...) -> (ReductionInputs, VerifierOutputs); +// default `prove`/`verify` chain *_inner -> reduce_statement automatically +\end{lstlisting} +Implementor writes the three; trait wires them so drift is structurally impossible. +\end{frame} + % -------------------------------------------------------------------- \begin{frame}{Where this leaves us \,---\, the next move} \textbf{Paper section (accumulator as typed state).} Describe the IOR trait as a paper-aligned realisation \emph{of WARP's protocol structure}, framed as a \emph{template other protocols can follow}, not a contract they must. diff --git a/src/lib.rs b/src/lib.rs index 150181a..9abaa7b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,7 @@ use error::{DeciderError, ProverError, VerifierError}; use protocol::phases::{ batching::{Batching, BatchingProverInputs, BatchingStatement, BatchingVerifierInputs}, ood::{Ood, OodProverInputs, OodStatement}, + oracle_handle::{IndexedOracle, MerkleIndexedOracle}, pesat::{Pesat, PesatStatement, PesatWitness}, proximity::{Proximity, ProximityProverInputs, ProximityStatement, ProximityVerifierInputs}, twin_constraint::{ @@ -152,13 +153,13 @@ where hasher: &self.params.hasher, _phantom: PhantomData, }; - let (pesat_red, pesat_out) = pesat_phase.prove( + let (pesat_red, _pesat_proof, pesat_red_wit) = pesat_phase.prove( prover_state, &PesatStatement { l1, log_m }, - PesatWitness { + &PesatWitness { witnesses: &witnesses, }, - (), + &(), )?; // Phase 3a: twin-constraint sumcheck @@ -166,7 +167,7 @@ where r1cs: self.params.p.constraints(), _phantom: PhantomData, }; - let (tc_red, tc_out) = tc_phase.prove( + let (tc_red, _tc_proof, tc_red_wit) = tc_phase.prove( prover_state, &TwinConstraintStatement { acc_instance, @@ -176,13 +177,13 @@ where log_m, log_n, }, - TwinConstraintWitness { + &TwinConstraintWitness { acc_witness_w: &acc_ws, instances: &instances, witnesses: &witnesses, }, - TwinConstraintProverInputs { - fresh_codewords: &pesat_out.codewords, + &TwinConstraintProverInputs { + fresh_codewords: &pesat_red_wit.codewords, acc_codewords: &acc_fs, }, )?; @@ -194,11 +195,11 @@ where let eta = self .params .p - .evaluate_bundled(&beta_eq_evals, &tc_out.z) + .evaluate_bundled(&beta_eq_evals, &tc_red_wit.z) .map_err(|_| ProverError::SpongeFish)?; - let nu_0 = tc_out.f.query_at_point(&tc_red.zeta_0); + let nu_0 = tc_red_wit.f.query_at_point(&tc_red.zeta_0); - let (new_x, new_w) = tc_out.z.split_at(N - k); + let (new_x, new_w) = tc_red_wit.z.split_at(N - k); let new_x = new_x.to_vec(); let new_w = new_w.to_vec(); let new_beta = (vec![tc_red.beta_tau.clone()], vec![new_x]); @@ -208,7 +209,7 @@ where let _s = tracing::info_span!("warp.commit_new_oracle").entered(); count_ops!(MerkleTreeBuilds); let scheme = warp_scheme::(self.params.hasher.clone(), n); - let new_codeword = tc_out.f.evals().to_vec(); + let new_codeword = tc_red_wit.f.evals().to_vec(); scheme.commit(&[new_codeword]) }; prover_state.prover_message(td_new.root()); @@ -217,14 +218,16 @@ where // Phase 3c: OOD let ood_phase = Ood::::new(); - let (ood_red, _) = ood_phase.prove( + let (ood_red, _, _) = ood_phase.prove( prover_state, &OodStatement { s: self.params.config.s, log_n, }, - (), - OodProverInputs { oracle: &tc_out.f }, + &(), + &OodProverInputs { + oracle: &tc_red_wit.f, + }, )?; // Sample shift queries. @@ -242,7 +245,7 @@ where } let batching_phase = Batching::::new(); - let (batching_red, batching_out) = batching_phase.prove( + let (batching_red, _, batching_red_wit) = batching_phase.prove( prover_state, &BatchingStatement { zetas_prefix: zetas, @@ -251,8 +254,10 @@ where log_n, _phantom: PhantomData, }, - (), - BatchingProverInputs { oracle: &tc_out.f }, + &(), + &BatchingProverInputs { + oracle: &tc_red_wit.f, + }, )?; // Phase 3e: proximity. @@ -260,7 +265,7 @@ where hasher: &self.params.hasher, _phantom: PhantomData, }; - let (_, prox) = proximity_phase.prove( + let (_, prox, _) = proximity_phase.prove( prover_state, &ProximityStatement { queries: queries.clone(), @@ -268,9 +273,9 @@ where t: self.params.config.t, n, }, - (), - ProximityProverInputs { - td_0: &pesat_out.td_0, + &(), + &ProximityProverInputs { + td_0: &pesat_red_wit.td_0, acc_td: &acc_tds, }, )?; @@ -283,7 +288,7 @@ where let new_acc_instance = AccumulatorInstance { rt: vec![td_new.root().clone()], alpha: vec![batching_red.alpha], - mu: vec![batching_out.mu], + mu: vec![batching_red_wit.mu], beta: new_beta, eta: vec![eta], }; @@ -292,7 +297,7 @@ where w: vec![new_w], }; let proof = WARPProof { - rt_0: pesat_out.td_0.root().clone(), + rt_0: pesat_red_wit.td_0.root().clone(), mu_i: pesat_red.mus, nu_0, nu_i: nus, @@ -338,7 +343,7 @@ where _phantom: PhantomData, }; let (pesat_red, pesat_v_out) = - pesat_phase.verify(verifier_state, &PesatStatement { l1, log_m }, ())?; + pesat_phase.verify(verifier_state, &PesatStatement { l1, log_m }, &())?; let l1_mus = pesat_red.mus; let l1_taus = pesat_red.taus; let rt_0 = pesat_v_out.rt_0; @@ -358,7 +363,7 @@ where log_m, log_n, }, - (), + &(), )?; // 4. Read between TC and OOD: td (new commitment), η, ν₀. @@ -377,7 +382,7 @@ where s: self.params.config.s, log_n, }, - (), + &(), )?; // 7. Squeeze shift-query bytes; build queries; Proximity::verify. @@ -388,6 +393,63 @@ where let queries: QueryIndices = QueryIndices::from_squeezed_bytes(&bytes_shift_queries, log_n, self.params.config.t); + // Arity checks on the proof's shape. The orchestrator owns these + // since they're whole-proof shape checks, not per-oracle opening + // validity (which lives behind the IndexedOracle handles). + (proof.shift_query_answers.len() == self.params.config.t) + .ok_or_err(VerifierError::NumShiftQueries)?; + (proof.auth_j.len() == l2).ok_or_err(VerifierError::NumL2Instances)?; + + // Build (sorted, unique) leaf positions and a map from each unique + // position to a row in shift_query_answers (duplicates land on the + // same row). + let mut indexed: Vec<(usize, usize)> = queries + .leaf_positions + .iter() + .copied() + .enumerate() + .map(|(row, pos)| (pos, row)) + .collect(); + indexed.sort_by_key(|&(pos, _)| pos); + indexed.dedup_by_key(|&mut (pos, _)| pos); + let sorted_unique: Vec = indexed.iter().map(|&(p, _)| p).collect(); + let row_indices: Vec = indexed.iter().map(|&(_, r)| r).collect(); + + // Construct the BCS-checked oracle handles. The Proximity phase + // itself stays BCS-agnostic — it only sees `&dyn IndexedOracle`. + // Each handle gets its own `WarpScheme` built from the hasher value. + let fresh_values: Vec> = row_indices + .iter() + .map(|&r| proof.shift_query_answers[r][l2..].to_vec()) + .collect(); + let fresh_handle = MerkleIndexedOracle::new( + warp_scheme::(self.params.hasher.clone(), n), + &rt_0, + &proof.auth_0, + sorted_unique.clone(), + fresh_values, + ); + + let acc_handles: Vec> = (0..l2) + .map(|k| { + let acc_values: Vec> = row_indices + .iter() + .map(|&r| vec![proof.shift_query_answers[r][k]]) + .collect(); + MerkleIndexedOracle::new( + warp_scheme::(self.params.hasher.clone(), n), + &l2_roots[k], + &proof.auth_j[k], + sorted_unique.clone(), + acc_values, + ) + }) + .collect(); + let acc_refs: Vec<&dyn IndexedOracle>> = acc_handles + .iter() + .map(|h| h as &dyn IndexedOracle>) + .collect(); + let proximity_phase = Proximity:: { hasher: &self.params.hasher, _phantom: PhantomData, @@ -400,12 +462,10 @@ where t: self.params.config.t, n, }, - ProximityVerifierInputs { - rt_0: &rt_0, - l2_roots: &l2_roots, - auth_0: &proof.auth_0, - auth_j: &proof.auth_j, - shift_query_answers: &proof.shift_query_answers, + &ProximityVerifierInputs { + fresh: &fresh_handle, + acc: &acc_refs, + _h: PhantomData, }, )?; @@ -443,7 +503,7 @@ where log_n, _phantom: PhantomData, }, - BatchingVerifierInputs { + &BatchingVerifierInputs { nus, acc_mu: acc_mu_first, }, diff --git a/src/protocol/phases/batching.rs b/src/protocol/phases/batching.rs index bbdcda9..a220f28 100644 --- a/src/protocol/phases/batching.rs +++ b/src/protocol/phases/batching.rs @@ -18,8 +18,11 @@ //! - `ProverInputs` — `&Oracle` (the committed oracle, full data) //! - `VerifierInputs` — `(nus, acc_mu)` — used to compute `σ₂` and the //! final-claim oracle check. +//! - `ReductionInputs` — `alpha` (the LSB sumcheck challenges); both sides +//! compute and feed it through `reduce_statement`. //! - `ReducedStatement` — `alpha` — the new code-eval point (LSB-indexed) -//! - `ProverOutputs` — `mu` — the prover's reported `\hat f(α)` +//! - `ProofString` — `()` +//! - `ReducedWitness` — `mu` — the prover's reported `\hat f(α)` //! - `VerifierOutputs` — `()` use ark_ff::{Field, PrimeField}; @@ -104,12 +107,17 @@ pub struct BatchingVerifierInputs { pub acc_mu: F, } +pub struct BatchingReductionInputs { + /// LSB-indexed sumcheck challenge vector. + pub alpha: Vec, +} + pub struct BatchingReducedStatement { /// New code-eval point (LSB-indexed). pub alpha: Vec, } -pub struct BatchingProverOutputs { +pub struct BatchingReducedWitness { /// `\hat f(α)` — prover's report. pub mu: F, } @@ -141,22 +149,41 @@ where type Witness = (); type ProverInputs = BatchingProverInputs<'a, F>; type VerifierInputs = BatchingVerifierInputs; + type ReductionInputs = BatchingReductionInputs; type ReducedStatement = BatchingReducedStatement; - type ProverOutputs = BatchingProverOutputs; + type ProofString = (); + type ReducedWitness = BatchingReducedWitness; type VerifierOutputs = (); + fn reduce_statement( + &self, + _statement: &Self::Statement, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement { + BatchingReducedStatement { + alpha: inputs.alpha.clone(), + } + } + #[tracing::instrument( name = "batching", skip_all, fields(s = statement.s, t = statement.t, log_n = statement.log_n) )] - fn prove( + fn prove_inner( &self, prover_state: &mut ProverState, statement: &Self::Statement, - _witness: Self::Witness, - inputs: Self::ProverInputs, - ) -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError> { + _witness: &Self::Witness, + inputs: &Self::ProverInputs, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > { let n = inputs.oracle.len(); let r = 1 + statement.s + statement.t; let log_r = log2(r) as usize; @@ -200,10 +227,11 @@ where let mu = inputs.oracle.query_at_point(&alpha); Ok(( - BatchingReducedStatement { + BatchingReductionInputs { alpha: alpha.clone(), }, - BatchingProverOutputs { mu }, + (), + BatchingReducedWitness { mu }, )) } @@ -212,12 +240,12 @@ where skip_all, fields(s = statement.s, t = statement.t, log_n = statement.log_n) )] - fn verify<'b>( + fn verify_inner<'b>( &self, verifier_state: &mut VerifierState<'b>, statement: &Self::Statement, - inputs: Self::VerifierInputs, - ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> { + inputs: &Self::VerifierInputs, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> { let r = 1 + statement.s + statement.t; let log_r = log2(r) as usize; debug_assert_eq!(statement.zetas_prefix.len(), r); @@ -253,6 +281,6 @@ where .fold(F::zero(), |acc, (a, b)| acc + a * *b); (expected == res.final_claim).ok_or_err(VerifierError::Target)?; - Ok((BatchingReducedStatement { alpha: alpha_lsb }, ())) + Ok((BatchingReductionInputs { alpha: alpha_lsb }, ())) } } diff --git a/src/protocol/phases/mod.rs b/src/protocol/phases/mod.rs index d1ed7fa..4872b46 100644 --- a/src/protocol/phases/mod.rs +++ b/src/protocol/phases/mod.rs @@ -4,24 +4,31 @@ //! forthcoming `docs/paper-mods/mod3_accumulator_state.tex`. //! //! Each submodule implements one Interactive Oracle Reduction from the Warp -//! construction. The [`IOR`] trait below names the five paper-level -//! components — Statement, Witness, InputOracles, ReducedStatement, -//! OutputOracles — and splits oracle types into prover-side (full data) / -//! verifier-side (commitments) halves so the trait can serve both roles -//! against the same struct. +//! construction. The [`IOR`] trait below names the paper-level components +//! and splits oracle types into prover-side / verifier-side halves so the +//! trait can serve both roles against the same struct. //! //! Implementor pattern: a phase is a struct holding setup parameters -//! (codes, merkle hash params); `prove` and `verify` are `&self` methods -//! that take statement / witness / inputs per call and return reduced -//! statement + output oracles. +//! (codes, hashers); implementors write [`IOR::prove_inner`], +//! [`IOR::verify_inner`], and [`IOR::reduce_statement`]. The trait provides +//! default `prove` / `verify` that mechanically chain +//! `*_inner -> reduce_statement` so the prover and verifier *cannot* drift +//! on how `ReducedStatement` is computed from transcript data. //! //! The top-level orchestrators in `src/lib.rs::WARP::prove` and `::verify` //! thread state between phases by chaining `IOR::prove` / `IOR::verify` -//! calls — each phase's `ReducedStatement` and `Outputs` feed the next -//! phase's `Statement` / `Inputs`. +//! calls — each phase's `ReducedStatement`, `ProofString`, and +//! `ReducedWitness` feed the next phase's `Statement` / `ProverInputs` / +//! the global proof object. + +// `prove_inner` returns the 3-tuple `(ReductionInputs, ProofString, +// ReducedWitness)`; clippy flags the resulting type as "complex" but +// the structure is exactly what the IOR formalism asks for. +#![allow(clippy::type_complexity)] pub mod batching; pub mod ood; +pub mod oracle_handle; pub mod pesat; pub mod proximity; pub mod twin_constraint; @@ -38,44 +45,119 @@ use crate::error::{ProverError, VerifierError}; /// (stmt, wit, oracles_in) --> (stmt', oracles_out) /// ``` /// -/// Seven associated types decompose the paper's tripartite split: +/// **Convention.** `Witness` and `ProverInputs` are conventionally +/// reference-holding wrappers (e.g. `PesatWitness<'a, F> { witnesses: +/// &'a [Vec] }`). The trait takes them by `&` so the caller never has +/// to clone bulk data on the way in. None of WARP / WHIR / STIR / FRI / +/// sumcheck-based protocols consume their witness at the IOR level. /// -/// - `Statement` / `ReducedStatement` are shared between prover and verifier -/// (what's claimed before and after the reduction). -/// - `Witness` is prover-only. -/// - Oracle types are split per role: the prover sees full evaluation data -/// (`ProverInputs` / `ProverOutputs`); the verifier sees commitments -/// (`VerifierInputs` / `VerifierOutputs`). For phases that don't pass any -/// oracle through one role, use `()`. +/// **Drift prevention.** Implementors write [`prove_inner`], +/// [`verify_inner`], and [`reduce_statement`]. The default `prove` / +/// `verify` chain them automatically; the `ReducedStatement` is computed +/// in exactly one place (`reduce_statement`), eliminating the bug class +/// where prover and verifier compute the same reduction via parallel +/// code paths that silently diverge. pub trait IOR { /// Pre-reduction claim. Visible to prover and verifier. type Statement; - /// Prover-only inputs (full witness data, etc.). + /// Prover-only data the prover reads (typically a reference-holding + /// wrapper — see the convention note on the trait). type Witness; /// Oracles flowing in from upstream IORs (prover view: full data). type ProverInputs; /// Oracles flowing in from upstream IORs (verifier view: commitments). type VerifierInputs; - /// Post-reduction claim. Visible to prover and verifier. + /// Explicit inputs that determine [`Self::ReducedStatement`]. Both + /// prover-side and verifier-side machinery produce this struct, then + /// hand it to [`Self::reduce_statement`] for the (single) statement + /// derivation. + type ReductionInputs; + /// Post-reduction claim. Visible to prover and verifier. Computed + /// exclusively by [`Self::reduce_statement`]. type ReducedStatement; - /// Oracles emitted by this IOR (prover view: full data, plus any private - /// reduced witness state). - type ProverOutputs; - /// Oracles emitted by this IOR (verifier view: commitments). + /// Out-of-band proof bytes the verifier always reads (auth paths, + /// shift-query answers, …). The orchestrator collects these into the + /// global proof object. + type ProofString; + /// Reduced-witness handoff to the next IOR — full prover-side data + /// (oracles, witness vectors, committed trees). In terminal-round + /// protocols (e.g. WHIR's final fold) this can also be revealed to + /// the verifier; the trait does not assume it is private. + type ReducedWitness; + /// Verifier-side outputs (commitments, parsed digests). Threaded into + /// downstream phase `VerifierInputs`. type VerifierOutputs; + /// THE single source of truth for `ReducedStatement`. + /// + /// Both prover and verifier call this with their respective + /// [`Self::ReductionInputs`] (assembled in `prove_inner` / + /// `verify_inner`). Drift between sides is structurally impossible + /// because both go through this function. + fn reduce_statement( + &self, + statement: &Self::Statement, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement; + + /// Implementor's prover-side body. Runs the protocol's prover + /// machinery and returns the inputs `reduce_statement` needs, plus + /// the proof string and reduced witness. + fn prove_inner( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + witness: &Self::Witness, + inputs: &Self::ProverInputs, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + >; + + /// Implementor's verifier-side body. Runs the protocol's verifier + /// machinery (sumcheck checks, transcript reads, soundness checks) + /// and returns the inputs `reduce_statement` needs. + fn verify_inner<'a>( + &self, + verifier_state: &mut VerifierState<'a>, + statement: &Self::Statement, + inputs: &Self::VerifierInputs, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError>; + + /// Default impl. Implementors should not override. fn prove( &self, prover_state: &mut ProverState, statement: &Self::Statement, - witness: Self::Witness, - inputs: Self::ProverInputs, - ) -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError>; + witness: &Self::Witness, + inputs: &Self::ProverInputs, + ) -> Result< + ( + Self::ReducedStatement, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > { + let (red_inputs, proof, red_wit) = + self.prove_inner(prover_state, statement, witness, inputs)?; + let reduced = self.reduce_statement(statement, &red_inputs); + Ok((reduced, proof, red_wit)) + } + /// Default impl. Implementors should not override. fn verify<'a>( &self, verifier_state: &mut VerifierState<'a>, statement: &Self::Statement, - inputs: Self::VerifierInputs, - ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError>; + inputs: &Self::VerifierInputs, + ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> { + let (red_inputs, vouts) = self.verify_inner(verifier_state, statement, inputs)?; + let reduced = self.reduce_statement(statement, &red_inputs); + Ok((reduced, vouts)) + } } diff --git a/src/protocol/phases/ood.rs b/src/protocol/phases/ood.rs index 8aee302..150027a 100644 --- a/src/protocol/phases/ood.rs +++ b/src/protocol/phases/ood.rs @@ -12,8 +12,11 @@ //! - `ProverInputs` — `&Oracle` (the committed oracle, full data) //! - `VerifierInputs` — `()` (the oracle check is deferred to the batching //! sumcheck's final claim) +//! - `ReductionInputs` — `(samples_flat, answers)` — both sides arrive here +//! from the same transcript reads and feed into `reduce_statement`. //! - `ReducedStatement` — `(samples_flat, answers)` — query points + their answers -//! - `ProverOutputs` — `()` +//! - `ProofString` — `()` +//! - `ReducedWitness` — `()` //! - `VerifierOutputs` — `()` use ark_ff::{Field, PrimeField}; @@ -34,6 +37,11 @@ pub struct OodProverInputs<'a, F: Field> { pub oracle: &'a Oracle, } +pub struct OodReductionInputs { + pub samples_flat: Vec, + pub answers: Vec, +} + pub struct OodReducedStatement { /// Flat challenge vector of length `s · log_n`. pub samples_flat: Vec, @@ -69,18 +77,38 @@ where type Witness = (); type ProverInputs = OodProverInputs<'a, F>; type VerifierInputs = (); + type ReductionInputs = OodReductionInputs; type ReducedStatement = OodReducedStatement; - type ProverOutputs = (); + type ProofString = (); + type ReducedWitness = (); type VerifierOutputs = (); + fn reduce_statement( + &self, + _statement: &Self::Statement, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement { + OodReducedStatement { + samples_flat: inputs.samples_flat.clone(), + answers: inputs.answers.clone(), + } + } + #[tracing::instrument(name = "ood", skip_all, fields(s = statement.s, log_n = statement.log_n))] - fn prove( + fn prove_inner( &self, prover_state: &mut ProverState, statement: &Self::Statement, - _witness: Self::Witness, - inputs: Self::ProverInputs, - ) -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError> { + _witness: &Self::Witness, + inputs: &Self::ProverInputs, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > { let samples_flat = prover_state.verifier_messages_vec::(statement.s * statement.log_n); count_ops!(OodPointQueries, statement.s as u64); let answers = samples_flat @@ -89,11 +117,12 @@ where .collect::>(); prover_state.prover_messages(&answers); Ok(( - OodReducedStatement { + OodReductionInputs { samples_flat, answers, }, (), + (), )) } @@ -102,18 +131,18 @@ where skip_all, fields(s = statement.s, log_n = statement.log_n) )] - fn verify<'b>( + fn verify_inner<'b>( &self, verifier_state: &mut VerifierState<'b>, statement: &Self::Statement, - _inputs: Self::VerifierInputs, - ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> { + _inputs: &Self::VerifierInputs, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> { let samples_flat: Vec = (0..statement.s * statement.log_n) .map(|_| verifier_state.verifier_message::()) .collect(); let answers: Vec = verifier_state.prover_messages_vec(statement.s)?; Ok(( - OodReducedStatement { + OodReductionInputs { samples_flat, answers, }, diff --git a/src/protocol/phases/oracle_handle.rs b/src/protocol/phases/oracle_handle.rs new file mode 100644 index 0000000..51f5594 --- /dev/null +++ b/src/protocol/phases/oracle_handle.rs @@ -0,0 +1,121 @@ +//! Oracle access via handle traits. +//! +//! Phase verifiers should code against [`IndexedOracle`] rather than +//! against a concrete BCS-shaped tuple of `(root, opening proof, +//! precomputed answer table)`. The handle internally validates the +//! commitment-scheme opening and exposes a partial-function view of the +//! committed oracle: +//! +//! ```text +//! query(i) -> Some(value) iff index in range AND opening passes +//! query(i) -> None otherwise +//! ``` +//! +//! This matches the IOP/BCS formalism where oracles are partial +//! functions; the BCS instantiation (Merkle root + path-pruned +//! multi-opening proof + the values that get authenticated) lives behind +//! the trait, not in the phase verifier code. +//! +//! Currently used by [`super::proximity`] on the verifier side. Other +//! phases continue to use concrete oracle types for now; the migration +//! of OOD / Batching prover-side `Oracle` access to a sibling +//! `EvalOracle` trait is tracked as a follow-up. + +use std::cell::OnceCell; + +use ark_ff::Field; +use ark_mt::{multi_vector::MultiVectorOpening, MerkleHasher}; + +use crate::crypto::merkle::{WarpProof, WarpScheme}; + +/// Partial-function view of an indexed oracle. +/// +/// Implementations are responsible for any validation needed before +/// returning a value. `query(i) -> None` covers both "index out of +/// range" and "opening failed validation." +pub trait IndexedOracle { + fn query(&self, i: usize) -> Option; + /// Optional eager validation hook. Phases that want a single + /// up-front check (rather than lazy per-query) call `validate()` + /// once. The default impl performs a no-op `query(0)` to drive + /// whatever lazy validation the impl uses. + fn validate(&self) -> bool { + self.query(0).is_some() + } +} + +/// BCS-checked Merkle handle for a multi-vector commitment. +/// +/// Holds the scheme, root, opening proof, and the (sorted, unique) +/// indices + per-index values that get authenticated against the root. +/// Validation runs lazily on the first call to [`query`] / +/// [`validate`] and is memoized so repeated calls are free. +pub struct MerkleIndexedOracle<'a, F, H> +where + F: Field + Clone, + H: MerkleHasher>, +{ + pub scheme: WarpScheme, + pub root: &'a H::Digest, + pub proof: &'a WarpProof, + /// Sorted, unique leaf positions that appear in `values_by_index`. + pub sorted_indices: Vec, + /// `values_by_index[k]` is the leaf-tuple at `sorted_indices[k]`. + /// Inner length = number of codewords interleaved under this root. + pub values_by_index: Vec>, + /// Memoized opening-validation result. + validated: OnceCell, +} + +impl<'a, F, H> MerkleIndexedOracle<'a, F, H> +where + F: Field + Clone, + H: MerkleHasher>, +{ + pub fn new( + scheme: WarpScheme, + root: &'a H::Digest, + proof: &'a WarpProof, + sorted_indices: Vec, + values_by_index: Vec>, + ) -> Self { + Self { + scheme, + root, + proof, + sorted_indices, + values_by_index, + validated: OnceCell::new(), + } + } + + fn run_validation(&self) -> bool { + let opening = match MultiVectorOpening::new( + self.sorted_indices.clone(), + self.values_by_index.clone(), + ) { + Ok(o) => o, + Err(_) => return false, + }; + self.scheme.check(self.root, &opening, self.proof) + } +} + +impl<'a, F, H> IndexedOracle> for MerkleIndexedOracle<'a, F, H> +where + F: Field + Clone, + H: MerkleHasher>, +{ + fn query(&self, i: usize) -> Option> { + let valid = *self.validated.get_or_init(|| self.run_validation()); + if !valid { + return None; + } + let pos = self.sorted_indices.binary_search(&i).ok()?; + Some(self.values_by_index[pos].clone()) + } + + fn validate(&self) -> bool { + *self.validated.get_or_init(|| self.run_validation()) + } +} diff --git a/src/protocol/phases/pesat.rs b/src/protocol/phases/pesat.rs index 4b23549..9624db9 100644 --- a/src/protocol/phases/pesat.rs +++ b/src/protocol/phases/pesat.rs @@ -11,8 +11,10 @@ //! - `Witness` — `&[Vec]` (fresh witnesses to encode) //! - `ProverInputs` — `()` //! - `VerifierInputs` — `()` +//! - `ReductionInputs` — `(mus, taus)` — both sides derive from transcript //! - `ReducedStatement` — `(mus, taus)` — code-eval claims + zero-check randomness -//! - `ProverOutputs` — full codewords + multi-vector commit +//! - `ProofString` — `()` +//! - `ReducedWitness` — full codewords + multi-vector commit //! - `VerifierOutputs` — Merkle root only use ark_codes::traits::LinearCode; @@ -35,12 +37,17 @@ pub struct PesatWitness<'a, F: Field> { pub witnesses: &'a [Vec], } +pub struct PesatReductionInputs { + pub mus: Vec, + pub taus: Vec>, +} + pub struct PesatReducedStatement { pub mus: Vec, pub taus: Vec>, } -pub struct PesatProverOutputs +pub struct PesatReducedWitness where F: Field, H: MerkleHasher>, @@ -76,22 +83,42 @@ where type Witness = PesatWitness<'a, F>; type ProverInputs = (); type VerifierInputs = (); + type ReductionInputs = PesatReductionInputs; type ReducedStatement = PesatReducedStatement; - type ProverOutputs = PesatProverOutputs; + type ProofString = (); + type ReducedWitness = PesatReducedWitness; type VerifierOutputs = PesatVerifierOutputs; + fn reduce_statement( + &self, + _statement: &Self::Statement, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement { + PesatReducedStatement { + mus: inputs.mus.clone(), + taus: inputs.taus.clone(), + } + } + #[tracing::instrument( name = "pesat", skip_all, fields(l1 = statement.l1, log_m = statement.log_m, n_witnesses = witness.witnesses.len()) )] - fn prove( + fn prove_inner( &self, prover_state: &mut ProverState, statement: &Self::Statement, - witness: Self::Witness, - _inputs: Self::ProverInputs, - ) -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError> { + witness: &Self::Witness, + _inputs: &Self::ProverInputs, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > { // a. encode witnesses let codewords = { let _s = tracing::info_span!("pesat.encode").entered(); @@ -122,11 +149,12 @@ where }; Ok(( - PesatReducedStatement { + PesatReductionInputs { mus: mus.clone(), taus, }, - PesatProverOutputs { codewords, td_0 }, + (), + PesatReducedWitness { codewords, td_0 }, )) } @@ -135,12 +163,12 @@ where skip_all, fields(l1 = statement.l1, log_m = statement.log_m) )] - fn verify<'b>( + fn verify_inner<'b>( &self, verifier_state: &mut VerifierState<'b>, statement: &Self::Statement, - _inputs: Self::VerifierInputs, - ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> { + _inputs: &Self::VerifierInputs, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> { // commitment digest let rt_0: H::Digest = verifier_state.prover_message()?; @@ -157,7 +185,7 @@ where .collect(); Ok(( - PesatReducedStatement { mus, taus }, + PesatReductionInputs { mus, taus }, PesatVerifierOutputs { rt_0 }, )) } diff --git a/src/protocol/phases/proximity.rs b/src/protocol/phases/proximity.rs index 31a366f..53fcbdb 100644 --- a/src/protocol/phases/proximity.rs +++ b/src/protocol/phases/proximity.rs @@ -12,12 +12,14 @@ //! - `Witness` — `()` //! - `ProverInputs` — fresh `WarpCommitted` + l2 accumulated `WarpCommitted`s //! - `VerifierInputs` — fresh root + l2 acc roots + opening proofs + answers +//! - `ReductionInputs` — `()` (no reduction) //! - `ReducedStatement` — `()` (Proximity is a check, not a reduction) -//! - `ProverOutputs` — opening proofs + shift_query_answers (proof artifacts) +//! - `ProofString` — opening proofs + shift_query_answers +//! - `ReducedWitness` — `()` //! - `VerifierOutputs` — `()` use ark_ff::Field; -use ark_mt::{multi_vector::MultiVectorOpening, MerkleHasher}; +use ark_mt::MerkleHasher; use spongefish::{ProverState, VerifierState}; use std::marker::PhantomData; @@ -46,15 +48,24 @@ where pub acc_td: &'a [WarpCommitted], } -pub struct ProximityVerifierInputs<'a, F: Field, H: MerkleHasher> { - pub rt_0: &'a H::Digest, - pub l2_roots: &'a [H::Digest], - pub auth_0: &'a WarpProof, - pub auth_j: &'a [WarpProof], - pub shift_query_answers: &'a [Vec], +/// Verifier-side inputs for Proximity. +/// +/// The phase verifier no longer sees roots / opening proofs / answer +/// tables directly; it sees a list of [`IndexedOracle`] handles and +/// triggers their (lazy, memoized) BCS validation. Phases stay +/// BCS-agnostic at the type level — the orchestrator constructs the +/// concrete [`oracle_handle::MerkleIndexedOracle`] handles. +pub struct ProximityVerifierInputs<'a, F: Field, H: MerkleHasher>> { + /// Handle for the fresh PESAT multi-vector commitment (m = l1). + pub fresh: &'a dyn crate::protocol::phases::oracle_handle::IndexedOracle>, + /// One handle per accumulated commitment (each with m = 1). + pub acc: &'a [&'a dyn crate::protocol::phases::oracle_handle::IndexedOracle>], + /// Phantom marker so this struct still mentions H (handles are + /// trait-objects so don't carry H in their type). + pub _h: std::marker::PhantomData, } -pub struct ProximityProverOutputs +pub struct ProximityProofString where F: Field, H: MerkleHasher, @@ -88,10 +99,19 @@ where type Witness = (); type ProverInputs = ProximityProverInputs<'a, F, H>; type VerifierInputs = ProximityVerifierInputs<'a, F, H>; + type ReductionInputs = (); type ReducedStatement = (); - type ProverOutputs = ProximityProverOutputs; + type ProofString = ProximityProofString; + type ReducedWitness = (); type VerifierOutputs = (); + fn reduce_statement( + &self, + _statement: &Self::Statement, + _inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement { + } + #[tracing::instrument( name = "proximity", skip_all, @@ -100,13 +120,20 @@ where n_accumulators = inputs.acc_td.len(), ) )] - fn prove( + fn prove_inner( &self, _prover_state: &mut ProverState, statement: &Self::Statement, - _witness: Self::Witness, - inputs: Self::ProverInputs, - ) -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError> { + _witness: &Self::Witness, + inputs: &Self::ProverInputs, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > { let leaf_positions = &statement.queries.leaf_positions; // ark-mt's `open()` requires strictly-sorted, unique indices. Query @@ -167,11 +194,12 @@ where Ok(( (), - ProximityProverOutputs { + ProximityProofString { auth_0, auth_j, shift_query_answers, }, + (), )) } @@ -180,59 +208,34 @@ where skip_all, fields(t = statement.t, l2 = statement.l2) )] - fn verify<'b>( + fn verify_inner<'b>( &self, _verifier_state: &mut VerifierState<'b>, statement: &Self::Statement, - inputs: Self::VerifierInputs, - ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> { - let leaf_positions = &statement.queries.leaf_positions; - - (inputs.shift_query_answers.len() == statement.t) - .ok_or_err(VerifierError::NumShiftQueries)?; - (inputs.auth_j.len() == statement.l2).ok_or_err(VerifierError::NumL2Instances)?; - - // Build the (sorted, unique) positions used by the merkle openings - // and remember which row of `shift_query_answers` corresponds to - // each unique position. Duplicate queries land on the same row. - let mut indexed: Vec<(usize, usize)> = leaf_positions - .iter() - .copied() - .enumerate() - .map(|(row, pos)| (pos, row)) - .collect(); - indexed.sort_by_key(|&(pos, _)| pos); - indexed.dedup_by_key(|&mut (pos, _)| pos); - let sorted_unique: Vec = indexed.iter().map(|&(p, _)| p).collect(); - let row_indices: Vec = indexed.iter().map(|&(_, r)| r).collect(); - - let scheme = warp_scheme::(self.hasher.clone(), statement.n); - - // Fresh PESAT opening: per unique position, take the l1-suffix of the - // corresponding answers row. - let fresh_values: Vec> = row_indices - .iter() - .map(|&r| inputs.shift_query_answers[r][statement.l2..].to_vec()) - .collect(); - let fresh_opening = MultiVectorOpening::new(sorted_unique.clone(), fresh_values) - .map_err(|_| VerifierError::ShiftQueryIndex)?; - scheme - .check(inputs.rt_0, &fresh_opening, inputs.auth_0) + inputs: &Self::VerifierInputs, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> { + // Arity / shape checks (orchestrator-side concerns the phase still + // owns: number of accumulator openings must match l2; number of + // shift queries is implicitly checked by the handle constructors). + (inputs.acc.len() == statement.l2).ok_or_err(VerifierError::NumL2Instances)?; + + // Validate each oracle handle. The handle is a trait-object + // partial function: validate() runs the (lazy, memoized) BCS + // check internally — this phase code is BCS-agnostic. + inputs + .fresh + .validate() .ok_or_err(VerifierError::ShiftQuery)?; - count_ops!(MerklePathsVerified, sorted_unique.len() as u64); - - // Accumulator openings: each is m=1 (single codeword). - for (k, root) in inputs.l2_roots.iter().enumerate() { - let acc_values: Vec> = row_indices - .iter() - .map(|&r| vec![inputs.shift_query_answers[r][k]]) - .collect(); - let acc_opening = MultiVectorOpening::new(sorted_unique.clone(), acc_values) - .map_err(|_| VerifierError::ShiftQueryIndex)?; - scheme - .check(root, &acc_opening, &inputs.auth_j[k]) - .ok_or_err(VerifierError::ShiftQuery)?; - count_ops!(MerklePathsVerified, sorted_unique.len() as u64); + count_ops!( + MerklePathsVerified, + statement.queries.leaf_positions.len() as u64 + ); + for handle in inputs.acc.iter() { + handle.validate().ok_or_err(VerifierError::ShiftQuery)?; + count_ops!( + MerklePathsVerified, + statement.queries.leaf_positions.len() as u64 + ); } Ok(((), ())) diff --git a/src/protocol/phases/twin_constraint.rs b/src/protocol/phases/twin_constraint.rs index e8ce067..dac33cf 100644 --- a/src/protocol/phases/twin_constraint.rs +++ b/src/protocol/phases/twin_constraint.rs @@ -24,8 +24,11 @@ //! - `ProverInputs` — full codewords (from PESAT) + accumulated codewords //! - `VerifierInputs` — `()` — the deferred oracle check (final\_claim ≟ eq(τ,γ)·(ν₀+ω·η)) //! runs in the orchestrator after ν₀ and η arrive on the transcript +//! - `ReductionInputs` — `(ω, τ, γ, final_claim)` — both sides feed these into +//! `reduce_statement`, which computes ζ₀ / β_τ via `scale_and_sum` (single source of truth). //! - `ReducedStatement` — sumcheck challenges γ + unchecked final_claim + ω, τ + new α (ζ₀) + new β_τ -//! - `ProverOutputs` — the new reduced oracle `f` + the reduced witness vector `z` +//! - `ProofString` — `()` +//! - `ReducedWitness` — the new reduced oracle `f` + the reduced witness vector `z` //! - `VerifierOutputs` — `()` (the new commitment is read from the transcript by the orchestrator) use ark_ff::{Field, PrimeField}; @@ -241,7 +244,21 @@ pub struct TwinConstraintReducedStatement { pub deferred: DeferredOracleCheck, } -pub struct TwinConstraintProverOutputs { +/// Inputs to [`TwinConstraint::reduce_statement`]. Both prover and +/// verifier produce this struct from their respective machinery and feed +/// it into the shared reduction. Drift between the two sides is +/// structurally impossible because ζ₀ / β_τ are computed in exactly one +/// place — `reduce_statement` — using `scale_and_sum`. +pub struct TwinConstraintReductionInputs { + pub omega: F, + pub tau: Vec, + /// Sumcheck challenge vector (LSB-indexed). + pub gamma: Vec, + /// Sumcheck final value (the unchecked claim). + pub final_claim: F, +} + +pub struct TwinConstraintReducedWitness { /// New reduced codeword oracle. pub f: Oracle, /// Reduced witness vector `z = (x, w)` — consumed by η evaluation. @@ -267,22 +284,75 @@ where type Witness = TwinConstraintWitness<'a, F>; type ProverInputs = TwinConstraintProverInputs<'a, F>; type VerifierInputs = (); + type ReductionInputs = TwinConstraintReductionInputs; type ReducedStatement = TwinConstraintReducedStatement; - type ProverOutputs = TwinConstraintProverOutputs; + type ProofString = (); + type ReducedWitness = TwinConstraintReducedWitness; type VerifierOutputs = (); + /// Single source of truth for ζ₀ / β_τ. Both prover and verifier + /// land here with `(ω, τ, γ, final_claim)`; the new accumulator + /// state is computed identically on both sides. + fn reduce_statement( + &self, + statement: &Self::Statement, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement { + let log_l = statement.log_l; + let log_n = statement.log_n; + let l1 = statement.l1_mus.len(); + + let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &inputs.gamma); + + let alpha_vecs = concat_slices( + &statement.acc_instance.alpha, + &vec![vec![F::zero(); log_n]; l1], + ); + let zeta_0 = scale_and_sum(&alpha_vecs, &gamma_eq_evals); + + // β τ-vectors: accumulated taus first (length l2), then PESAT taus + // (length l1). The τ component of the new β = Σ γ_eq(i) · β_i. + let beta_taus: Vec> = statement + .acc_instance + .beta + .0 + .iter() + .cloned() + .chain(statement.l1_taus.iter().cloned()) + .collect(); + let beta_tau = scale_and_sum(&beta_taus, &gamma_eq_evals); + + TwinConstraintReducedStatement { + gamma: inputs.gamma.clone(), + zeta_0, + beta_tau, + deferred: DeferredOracleCheck { + omega: inputs.omega, + tau: inputs.tau.clone(), + claim: inputs.final_claim, + }, + } + } + #[tracing::instrument( name = "twin_constraint", skip_all, fields(log_l = statement.log_l, log_m = statement.log_m, log_n = statement.log_n) )] - fn prove( + fn prove_inner( &self, prover_state: &mut ProverState, statement: &Self::Statement, - witness: Self::Witness, - inputs: Self::ProverInputs, - ) -> Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError> { + witness: &Self::Witness, + inputs: &Self::ProverInputs, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > { let l1 = inputs.fresh_codewords.len(); let log_l = statement.log_l; let log_m = statement.log_m; @@ -346,28 +416,24 @@ where }; debug_assert_eq!(proof.challenges.len(), log_l); - // d. pull the single remaining row out of each tablewise table. + // d. pull only the reduced *witness* halves out of CC. ζ₀ and β_τ + // are NOT pulled here — `reduce_statement` recomputes them from + // (statement, γ) via `scale_and_sum`, which is the single source + // of truth that both prover and verifier go through. let reduced = cc.tablewise(); debug_assert!(reduced.iter().all(|t| t.len() == 1)); let f = reduced[0][0].clone(); let z = reduced[1][0].clone(); - let zeta_0 = reduced[2][0].clone(); - let beta_tau = reduced[3][0].clone(); - - let final_claim = proof.final_value; Ok(( - TwinConstraintReducedStatement { + TwinConstraintReductionInputs { + omega, + tau, gamma: proof.challenges, - zeta_0, - beta_tau, - deferred: DeferredOracleCheck { - omega, - tau, - claim: final_claim, - }, + final_claim: proof.final_value, }, - TwinConstraintProverOutputs { + (), + TwinConstraintReducedWitness { f: Oracle::from_evals(f), z, }, @@ -379,16 +445,15 @@ where skip_all, fields(log_l = statement.log_l, log_m = statement.log_m, log_n = statement.log_n) )] - fn verify<'b>( + fn verify_inner<'b>( &self, verifier_state: &mut VerifierState<'b>, statement: &Self::Statement, - _inputs: Self::VerifierInputs, - ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> { + _inputs: &Self::VerifierInputs, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> { let log_l = statement.log_l; let log_n = statement.log_n; let l1 = statement.l1_mus.len(); - let l2 = statement.acc_instance.mu.len(); // Squeeze ω, τ matching the prover. let omega: F = verifier_state.verifier_message(); @@ -417,9 +482,7 @@ where // Run the sumcheck. The deferred oracle check // final_claim == eq(τ, γ) · (ν₀ + ω · η) // is left to the orchestrator because ν₀ and η arrive on the - // transcript AFTER the sumcheck rounds (between TwinConstraint and - // OOD). We surface the unchecked final_claim and ω, τ, γ via - // ReducedStatement so the orchestrator can finish the check. + // transcript AFTER the sumcheck rounds. let tc_degree = 1 + (log_n + 1).max(statement.log_m + 2); let (gamma, final_claim) = { let mut wrap = EffscVerifierTranscript(verifier_state); @@ -427,38 +490,12 @@ where (res.challenges, res.final_claim) }; - // Compute ζ₀ and β_τ from the sumcheck challenges (mirror the prover's - // sumcheck-fold of α and β). - let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma); - let alpha_vecs = concat_slices( - &statement.acc_instance.alpha, - &vec![vec![F::zero(); log_n]; l1], - ); - let zeta_0 = scale_and_sum(&alpha_vecs, &gamma_eq_evals); - - // β τ-vectors: accumulated taus first (length l2), then PESAT taus - // (length l1). We need the τ component of the new β = sum_{i} γ_eq(i) · β_i. - let beta_taus: Vec> = statement - .acc_instance - .beta - .0 - .iter() - .cloned() - .chain(statement.l1_taus.iter().cloned()) - .collect(); - debug_assert_eq!(beta_taus.len(), l2 + l1); - let beta_tau = scale_and_sum(&beta_taus, &gamma_eq_evals); - Ok(( - TwinConstraintReducedStatement { + TwinConstraintReductionInputs { + omega, + tau, gamma, - zeta_0, - beta_tau, - deferred: DeferredOracleCheck { - omega, - tau, - claim: final_claim, - }, + final_claim, }, (), )) From 0df5adfa7f7115bcf3ea1ce4fea3e9161935bd8a Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 5 May 2026 22:32:09 +0200 Subject: [PATCH 28/68] remove dyn + fix slides --- docs/paper-mods/slides/iors.pdf | Bin 317810 -> 325389 bytes docs/paper-mods/slides/iors.tex | 221 +++++++++++-------------------- src/lib.rs | 11 +- src/protocol/phases/proximity.rs | 32 +++-- 4 files changed, 103 insertions(+), 161 deletions(-) diff --git a/docs/paper-mods/slides/iors.pdf b/docs/paper-mods/slides/iors.pdf index c111f5de49befc4ac36578aa6e3ba0328f1aacf1..254f8090463d15925449628f39c99dbc7229f7ab 100644 GIT binary patch delta 261349 zcmZU)LzE^=n01@BZM)L8ZQHi3H*KTRS!vt0ZQHi_f8Bj&cNVc$jL+HoiHL^=)Yu!; zI%)(EE~d2k6)^Lp$kS-9NV{TiJPLw?q47Tju@QsApd1Y-yMD6IM7JT&)%j9w zp1R*zQ>on0UBXNhrq;#z6lb||W;}Y7f+2YV21>rVD;2if)!W9sX5|!G>?O0YH^xkp zIqdJ|V3vYWMrC*OEv;iX!p*FIwnB-6S6CUHe%J~4gcA*wG4G%(G^;^={Jd}BVaPpe zho8VB=;9DSRxEnq?c8Mza5f=<`pfleMy4EZ+a=8ngwG{iALJ$jIbBDBLN2>UtCAc4 z&p15bFl}L}vOqA>^W^42Rz+5N z5Pq*Ed;c23QNQz{MYMIZMdeM5+Pu(dK6@5WiS9W7Q$)xW|K&pZc2LVrMr9?{fTm+& zx(88+m(q5zwVa$$0lFCsB}FlmMU2m*^CrL5Po8tOFPNVqNkvjCtLD_78_NmY5Yhk= zDI_2sZ;C?ERf~d;;RM!nR?yYM+~^5|`N15jLvR(!)1N`Ec+(vT2gMTQaQ-R3{GY3f z&NVk+5y3P{iGr>qT^!V?{)}Tg1ssb;qW^{Pj{R~L1`#n&pG>&>;t~ic6IQmTqm4yQ zt2+E7H_2LdMYkEmEKQ@XhB=RR$HTm6$ONsF<`F8|nyP06`K|mq-oWPwUkK;Dos4@XGa{Jc4%u&|WQZ&ib5qRvQ5idHJYw=QbZe z`1BH4Bwi;R;;o$;(R(M2pX;ezd-sj24od|^iz4{U1sms1-zmC!aIV&zYOuXw7T@ws z#nVd(!V@p9({Ce=2sV&wNh2XUmyu%sdcY^JaPvH6m~}Y2QXe=MEnfnWf7!l6liNH9 zmobF2k>E5_LD=j8A00Z^yqKgTM_*g zSs>k_t9h`=n=OIUB>TUSq!6R{S@6pEer~Yxf!a(PjT}XXgakO|B?^Z9bipVx)?wig z9bwt)tW*@|SoyhkFaCF2iiC*HWF!WMB|#fYzk_ra0R6{2d) zBRq;O01QUhCgV``QbA4%-bYC#i?k;%lX@hhG2UWDMlzn4@UxYC!yVWIj9pM*67L#h z=U?gb{KfE@56%O!l=eFfD2ZkyQ)WVSevxJlrG1~dgD~6}0G|MEPFF(QT|!q-wjPQ? z^?|6QM5xk{t@MtCgUfh>a}I18dS&H($rLOxz>b};R=sgJ{OjQAnH1bmPG$y+LP z72fo=!YdV1Tj%dL7p%ts_8ygH%MAXcrM{y{gGO|hqWIbs>nZP_(oLxuwi-|C>g%g} zfJJ-P6zsXF!#X!q59n9IKySH9MCXRvX!-7-RA>m#)Tkd$=M>7{>CD61T+wFX)E;p?XD#0zI|5`z%edxap^wKlD~enXEk#xwwS@M_L7iju!hP zq$pKL0Ex303}P{JY*u5|`T<9#r;co>liY>WYr){Pvse!Ly}t@Uxk^VPmz1Xv07>_# zD0-<6x}}e9*C0Jf3;Cc=6dzL8JX)@8zXc%ynFcOIhL`+}66Rxkh=?lPxvgV`o|(r& zcaVD4+EH$eLxvozbmxqMbR&bd+ow>WDFo4L<+Jde0TJe!rs}1~~eg zAgnn6L^}^v!bn^Hq^m}oST>Usuz0ZT0p^>$fGpp9k1u_j%sk+x5H=YC8_3?s(FV0- zg8v)_6QEJf-~TK1u;VlofVQOstyT@cOIsSB2gowL&{^kcms`2|O>7ohh)lX6=tl~= z$f*Eg7))0<1W#goU-rzv*N1*TP`$`S&ARt>%PCa0hj?JCSyZ>)UWoPppzw;Kp#b$` z)2u%_v9()gm4BmQ_2H18+q-p)_l!^svq*i%nYZrKzc}tbBJJyw^WEtgdC8u@Mdt{M zx$Y*Si!9^TfoM~z>@969)^hHHe`nQ)KU*k3%38*F4Q94X`gH35@MXzz*O5^4w?4Auu3O*m}D} zoS!A1Ym+it_4=HJ$>Vi@omj(}Nt%*K{+w3A#C&E;y62a-_(c&-3Z!0YL@&2&6rpS@ zvX^~wCg)3iQ8x+L)u?Kixio6o+XS(mb|s)a;X*n9S?=?_#k{0K(y5O|oZ-Vnlg3&g zia)dBwN6?^(MdW3RxnHMjWh2;N9H~8-(et~@zys%-+Lr9088!3q=)JXwG-n8Ytoi` zgSX2qYAJ(kR1)oJN=1LuZi&S7P+7>R!4yWLzH%Zdfx_zO?w^oRY8~!h|CqSeaY_XW zPt(r~);LS*+irD+CtN0#h0d5<7B$-RcaiIZg!#@8tl~yerRGaN2407 z`SD*CH?%fkLaW+uUzQ|gf8Xod%FmbE2L2hlu<3(G^1S#TTg^-&&!CjS?ni@b9yzI3 zauq2!4>Qlet)Yf>{z87i8tO<#BwJ!qfiN@uZ!c)d#b=8nbfR#Qzb)itZlq%vzq6@Q+zcUrhf! z!U9a7Mq*@&y+w)%J8@XvFBaa&%j62i$B~UPaFZaWWTcG@Z+X$A1(M!QI!>-(hIl%!T40eU_90kLvlubZ*71717-4ymznsY zvYFLi)}(uCvw0O@K=6+Ua z0bJ;BqgMxy(p#}Il=I`ccyOgWJu?~^bLR_}d*kcCRT%G$aOcImt|D4AuyxMyNC7|! zXx?BAMDmZYR{-`E>!E`$Q25gu{Gw+^grw~F>)^}SyNS&^ux^It!z9IOOpqif%sgRq zL8xz_m9>5$i%RisxcIy#|2R_vp4ZCYw&%XwYCCatSux$&MPp~-a+W;F9@XXQ5Wc~` z+Afz>^#@79Be00y%d~Yi4=D%@k_$l4A5Fo`B~1H;;^=6Z+*>S*P~*V#@!5Tz*$E7E zJIaRwwj7vsGWqzZlWykHisVNf8S!kG!EJu2F`$ME=elWBH2^o5)dC7lgmP*){^~Z* z?<5)6dJux|AZegpY?aeXbJ=5?#NI+L$Gy=Z_$tr5y-d&84qqfNFo1=b;s)3#HdxoN zVGKvycxF^ImIi??0wL5gg!I~VD9{UlQ-GpTI@PV9ntJaCZ5HccHyp~vz|;kzZ>npH zh)2enWvYE~Q@DA#YvL9`nAO*Nr6qC=0-mvjjbgKOt7Y}R{qv;uRB<_{*; zXG{SD``q->F56o07B3}N7$B!uTl*FGsm?n1-km)MS#=wRg`?&!XYMS< zG=C`32~bi>ez4u?=H_uGP!={RsRAKE*iDh3Yka^fH6M(?b%MSi^8oz%@(w}3!t{CqbjX?tcy#bzDX(zW z9lio>ztP|$e`6Tg7QS&4K=qn`2||WZgkYJSJ7vBHs*R5W;J-snF!o}rY{C_V`!Y}L zjUeku&@P&@UN0@EtN?Y&x*e3nN1MgdhF*k~!>Be0)RF}Sq$_x}6QbCL^d*`0 zc2(Ma1S$w>o>j0dWs<+f?hI6$RpXa8{k)IT!5(3}lLGY+Ux>`F&2Fy<_K3+%I7M?U z7Zkjj_WjdL&)aNyc>FD$74_?DQ#Wzi3d048A%C10?!fg>LS#bDnXdcg4vPU9OY3bJ z9h^(Z3 z-7Q|62L5wZIbO7Y{lg=t0p1fSbY*f81u8Kvg)Y5wI?7&yH@5$gRYK+PNYw(-A(O2G zr?LdBl4)?z8oqeY3-awp8sOdZ3csYtX2^jW-OtQcY=#1XEzhTr*Y{t zz(?!yrV$ZBL)_KHUG0mkX635-mh0BG2utwm&GB!`7Ya4r#ipuW0s3LjmJQgtkShR^CK|A2tfoZxp;Q^W8^f(x7B8PAx=lpxaizBH-X}{on*S? z`8WOdgRuW+Xe1g50!XGiWP%C+jm(%k7eUPcG^9E}XZ+{}{Th#E1za6lD~XMlph$|M zeo{&Q>i(AwC*k`-=3wbn%ZBo@gnn}d(UfZJAE#r(8BgabJmKU+&1_#lW2dl*M6a7p zVYvP$@RwW1D{qSD->ILeX)UGd7TABgGyacUc-cWTq_uNyg0q59mSe^MpY>VGW&C_9 zoo?rjQ~Rr$#(BgO5yR=Z+W_;5vM3-on~xdH>?wii>^N8 zZR8jlOWNON^pL|CbSaZu1YwAWY1CcU4jN?23rJ!ac%c+%46<=<7R6K*OzgBQS_l5J zp=c|xOV9fNkz8p}un-^s(cv73BLk<;d={1mo;}Cqm03-sJ|RWH zH}H?+n%W1q3<1qO+TUpYTUV7<$TYMMeMWPX81!-pfYb2;?#U#eWH{3$FDMReWot!C`U${6c><4&QAP`%lJ5?F2EOpq8v9j*(l)oPr2V z2A)0m?kQ7iFwr8D2A<$vP?V`G)vthpVU9C3p2`q7^DzbtKA0X5A2=r|RVECcbKcnX z^=w3eOtDroIAkp#TPKlI#J0m3tme0b>1y<;(-KETdz{Vim&tI|+Y0}FEsZB!uaWK& z`KJ43r3Uppd>uoiTBkKF_?le#OV7Nc$d}J}9pN0gMC4DR+i24W%8|1=__a3g-1?Ja zMol)(3B9cOj6w6IaE3w+VIpp>B|Y+hIESQfN6z~74F9z(gDJRm z=e+S$>!QoGNBV+dY>vw|mc8_AUcgB?wZzWF$_<~Fo+QvhTuC*AslWFzvopZdZjRt8 zMy{ERr(0WrXB<3pnh-R9f)bRO^MB6*G-X^i*^zoKH5{F(xu}hJ z-z5=pTC7l7r<>eZlSQ^cNrg#pBU7X%qCPJ>qamLoobO$2c9RQRU=zbkwd)hk&P_+@(FnP|XCdUobwBiyn{bA9SwnBfxL{ei(7Ld(t%o7~MS> zaw9q?NnhB)4J(}&!kyRKC zM}+KEM75H)*>M5PB+0USsr4~rs^CmpS|eAf-3xx*mKWy0;*>PBwvYS=yxIEt>$R|( zzMek3ci+pmp3zPMqBLDP;xEER5Vilpt^*pY+t1ytEG~9h)Qm)>QHFy~_M4nDA8T^+ zClQKctCr^q;|;$up}nj+&uTz%ak`sx{2MdXykDUGpd$dW89+y-D_?FJwLm1TiK32k z34Mt0|JFtHG9LlKhu^T_A&8lz`_NueVoDX-#$oW z$y>J{HroKpKCAg}OC)XXHNMtqo^8L4;QH|0Iy*ZH^FT|-{LeJ`iuzqxj(2eHKt~ed zfwxPg`}*qXmTer|)}G4upMf7f5 zy5{c3<&JAPZBA&+@yM|hgI&{df>4p{qTd$im%HbO8#y&aYxUwRE?QfV)X6UHsHlESoC@hE?15&{^DXPLi zglVql8}`6~USJa3?1r^)6280*fD}R7Z<11uk=} z*TQuGjQBTX!7~y)NsE4Z?>d3n-m%s(F>NBrI)9^66g=={_sQxZwXH0?=HZ* zcC0q)8OL@=_UURzpVRw%NcSWp_?bx~Wd4Y%2rk2VF!~w;bU&0$EV7%eC}g{f+=h_- zN9f#G^tf+2cBF}WZMMDoLH)%>4=$Lg-6s3ZWmE7R$$#Y4vYuJ&AAk1EHYhY`ovX}s zx7x@3g~#b}RFY|$n69|xMt^VBh>Ln`s_i8V>>HO}PG-PKI~jG|UH|0xeFWlt=qoS*AY?aeUB){2OMu#|6hgY9#<%Bk z>!yp|UY5?wT6bh9;*gpTpt#1yP8N%q?2Hr?3OEg9@@(p0L|_BaWfXo0jhS7!Gs134 z8PfkOvvYhEI@Xfxe=;!QzYLTqq=~nu`2E^9;=T6Tc?@FF!NdZ3LI~7_U&&PG>-4rP)>a=Zz1e&KZ|uv+CS)oTxl@c-P>CHXr^{(a z?)z{O1Q9V2GZ&1mNyOZ^IgNI6SD3{Q{n)*sAwWW5M*Xi?h65>N*k|e_60G-y|dK{OVUHse|C(CJa_Dnu3 zxOWL=*l^l+Krh8(!wMR#9bv_5i7Vxdel+=*J@$B>9cMA=cN8qIibi~(EBxW-i zB@-_HZjI`)lT^*CM?Egh!A%#CihOKev&?g3v11)dnP)KJ!}Qhd=MNO^iRnB>d0QN+SqI0yla^SJI(!Cq1pJI)W56W#q!?;gSK(l0t3DF@=1?7;w^_iG5eU+qE zx2(f-U;!;*aP|iyLl&MY2o|MI2fCdzx6(dov#BefDXP*`CU!@&Age&e+ z_1aHzn!9KOr>XjIsrUWh?Rqi+tYj?$pv}UdLkKhLGWp){^uWH$@n^2%8Dw%W%18r` z$|B1F4FkGNrU2QWFNv8Tp`rG(s{`>2la;dOUEyqXCfqgH8x4)|t*n#RugMrciMmA6 zpg~SCz;@6L!mTH(1?M0i_83kZ^D^qtcBd*%!GfW8zew6AS97o0nvUCR@Ur3);8qXk zsMIgku)5}q4Wo#(p<#_oR+*v~a5;!f8wOE!Z@B1ddfHP-V@;Q z3ZoA%-3NIt*LrDuD*)l%)WV+%_&50w?cqLj9s^3HCr{tbDim&IfRkOYfK*Dx3oZsW zkE_02Nf7`uW$cH_00I`280zumh3q)cC%fH0A%Z{M2Wk1t`Z!Q2j6GbBj#kIP1LZ#3 zkG2X{ysG%j=g{eiT`Undyq6rc`Il*dFh61r|NWnyGP1G@1g^Ie+Zc=xU^A%h`9Qxa zy|viIh!U}iytU0W>iBXTp*-I>l)62w8YvzbY#Y{3FqsR_m@GyeuPOOmpCd~eXZNlDU(BnzE-c>+Cy;il{JBz8EqE&ckk znRs!WEW1;_LlJc;Q9z{9qSg=)utL3|=T|ew(cu1b77w(HAdFp_T?TtVkh+XyoUmm` z5ec+sm4=OP*MV90o+tNH8eR~K*|?D*Mdr~(vWfspfcvGsLz-jepmdV6wR;jA?9I&7 z4X^m~{gmw?(_S$N6G%2PslyR(%1JA|!1K~4IJ9gQOK*y~q#{nrv(>3g!dSA@;b+lg z4i4w{>$Y{h?@U8L`cZMeHR=)7FS3x8zVC$QVC`IM;NFGs2XrpCqC@fXR*E zgPO$9ZW&u9ZI%n1;RISPhLfCVgs4dU?zmEh(xrk~3_AVD4CeStvrXvyNAUvmjW@8E zcB|9OPC(1!Q!*s<({a!{rK{{7oM|knuEGrZ@tYnM4RzK!B;A@m^lE z%p*0cOmF`EJj)`j7pGm*D&nykx@DIOLNNyTDQc^C1}}m$Y;PFubl+Ohfqf6O`&Uy9 zV7<9oOH~|FxgV~ywl@At8TK!`SiX53Q09y< zFIcMSEZUUHsWhlK@+c(eX4hEFu6%9odXKhmPc)(AAYv{O&jF^Aoe=-4?vW!$>wcmI(h*A49 zS62VxC+Qq2h-cBrnByOUJjCn`!f?4?2qrN;KUnx^Z8aW-lNIU!v%KOKNE(>Y1ion- z7UvlX-CFHz`Nx5d=VK0q#5o23^GIuC+ib#F92GLHj)(b%o^MxP^IxkkfN}m+bPj0< zZigtlV@cBMWTu5OR@vHtYmS4??OL%qW<)Q?=&eGu3+$O=jg9^RAb~lYCrMBV(-!R>FhaUTmnD@TBOs&g@9mY;4V*(1Px z|0*jwBO*@vNK%5Y5}2$!UgGw1MSvKut;FdPE@$Cpdt){X>-i${gJ3niBHQg(%WLqf7LDPp1|=t&KU1p+-Roib4=*rjeU zL{z?Y&7IlnUbDV3(5TQFz~tHGU;r>YuxE5y%HHfRZ_!?>+$AtcAc0;2Kh6SR_#zZh zDP+fDC=-gZM?>{#6+Pwj!8Z*ZhsGShEp0(-6v+MQALW3Cy>&cLa zrFnofo#HEpgH>IC1A`K9f};sFM<5ZmSdE(_jJvF)T8!f0sBsp%&7+)t3zgT4aC(4I zywekPB9geon+KW<&7iNIZ4|Rj^aD*)yX1f!z7vjmB6RIOLoKP?AT~j}os)bA>S zS+v>Z-&0kkB4c|Ux^0GV7xSDuMrs$wQ4N5U`{i(mC8q?#NEoWT8xpLT8j+{qa zr>+Fe6vxn~0KOwguxbTtUjQU$4Jo#Et2Mg$@c`Y{{?4&u3aA~sLHjhXF-;Zwre1XP zR-~+~p=1RU@!tfxOvS5X`IAVwiQ#jhB)C*rB7x8wteWVl$c+NsN;` z8j;?>Z2{+L%7K#o`Q1n0-{`M3yG`_syLp34JRof@SZ^3b+KBo-;(n8n$dWBe&z5>q zkgmdP=FW)FpR0z>%rbr&F=>bwxm+Gr=%Xv}k=Eg|EFF?di-PI|nK%#kOq~2~P}Y zoE@}c3n2KayMiXw-NKL9Xe=-fLK7pfeLuZ_D)OIo!eKaJtVxR4LyDYM2V-ZVXXr`^ zyr2c^Ax)Y^*;ppNs-l#sQVLajxvWx6or%TzH8)V9*%REB)>e#pgD+&ByRm=~@P_T` z)mR;R{Qi;akMzx)=R=oU4xa@9w%O`^*0z5q*ApYpU^ih!>gh8O{V)2JXqrWmPb+&54VEiquQksv!Rb zIAI{e!EPb1al68$g+geUVV6_8Y5IeWK!d!#7w3ex7EjunkoV1 z(B_;gshZi6;XL3tHyFt`B2Ef*f?FyR>ssfi;i~3mwvpZw^-n*qrx#!~7<%gGHgA&s z-5Xk7Nin01&yM5T(56&INA=i&{K&&(a#$bp_sAb{boL&ztq(^oA9XyO*;_|r^}Vxz z_7UfrJcJV#E3{`PBftRR%J-nBtEST03nmh4g;BIiN1kXYpY3C|fbMaG`*@T26X#>; z;V@)-a4dEjA}j3gZy}{<#ja|WQAh|^mcrRVVha_mgQCOt9ea9mRO*E}{r&G1xnBL2+=jU4)4k;HW4ZE*jP{a9Va)U-_NAx1$9ilgTdJ#wLiP=OGb zMeLNlG4mEk(O~U>-5z!_D#A2qJ|K~(NRhRm+7+B=Uhtpj9WUGG`qY!m3STW8$Z6T&}JoYg62EIY?M^$y?Ud|v-9(<#oL*l ztldl_^!l<5lE4b59c+=KR!S$tL!maJ7r^?rUzwxyfkiM2y8Mmkks?%@)*l6e3c|?t zpAevea&WT$-)Kq3Ve@}qvLf*nnOfJpj!U3mNiJ~8%?X#A93319%A^d1{7gcb6PGQ6H4`ccH;kp+xS zcF+1O<;ST8QBmX7pc&CRS1e`Re!mCzj(lf)vit%jSyDz(s6j9eFknLuUgpkLa-=aO z2?nK+q~mymL{nKU#651Ry#eN|_e7rUFbRS%VNa44`K&!|zKbgVUeklE5PrGhBM!DQ zYQi!Yh-eVJBX7kA;0@{oeB2v69MQlLOvu#t8Yx{UDe_GbFp>8IUFFSZ%0Da8iwzdm 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®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

c~aD(oee+agCQAQ0tvh`^VGA2kxHhX*$G0}{|2 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℞ro{bP)Q(GkpMdoHa;hO*VoOQ!02~YN$@7RGJY&wd? z4ciQz$jSVEoLT``e|jNNAlnL=x?NMqk}!!YgKrvB?fWD#&9Ri>={9XIc-Hc`uZ{)} zdueNv2})cDhPb&}z^MUE!XuTtdmS%-$1kyH+$PO*9I2svGCMx0^>9)OMGTW0hW37| z{~pl{K38tr>wh>|OZqs`*cR%zMC1_dm|Q9p?ATX)PYmXke+~}Z8+4GcU-iVVp9v4V zgNKy%pl}sOm98BNFMbl+%CRo&0<3;9@ljG9DrvU|*#{WM5+62)tQ$%G0VXHQ3lz*v zl;ofxp}>B8Pg06PzNM=o<~zf7n)isIgiMu`*?_+3`1K&RBr^sPPg=wC3_aD@YydM&+H{0HQ#dNUS7_A@4C4%Atw?f6V zv$%IrC6DHBckD~m8rG_QY$c9q3?~0T+CMQ(pM=}%f8wzTTJCIKL?@#)2OXmC7x5VR zq0h?o4e&G-yF^sCSG+tMo)A%1~&i5fghtiE#qDhT5>vB)ot$EK>dt9rvOldr=y z5`t~m>&n1ZFTn31Qy%wIB(`nPa2=odInG#0V4>HPM0?=i*^flD+TP~*qU3b&FCR(M z9;DD)e|J2B26)}G9{aOUCa&W0*;`}#ywnF$4KN)7_I!x)Vuo=i7-QHj zoll*tX0-Pw>W=sN)VinQR#<>3G@*FiEOQD}y=)~bBw{(#E4v7EXH9_M|4GG|aQ?bC z6Jes!DzWUU>x$DJ9pST~|95|*!)a%TONSYaf0sAM1qio^`3@N-v={`UTcS9`4v8_Z zFk>gV38hbn_VNimNPgXPO&ObG&E)dAy*(gQn|+^8g9^~9RkiSvy3x@OS{yeL@Avu{}xzePKzQ6U{=B9_0u?^;apH6CVs18gWx ze^9LjK!}?1@!WP&#*3Q!`4aAFewP86ew-){)Ts)0Cps>aDWk_B`B#G-D8s1J!>=g}~t-xfy&R!UQ3Vk8)p zGo-jDQgu`a;*o-_NkF;&K&QhmVLMQMp6`;UuIh(G+{f0nd4@<9ymLX5upa5>iQcb1 z-Ld=zOG14X-e^<$aYZ$@?e~tD4`!=M!H*q}b??d)@s6oP1|J|a8dXktMV^5*e<$O2 z--JmuX4dFMX^QxW<@f{muy;b^J=&YLrqmCOgi@=2@+%MZ{oO}%CI`@{vwv`jfU*fG zV0Q{JrcNNRLjQK#!(|)&fVgowo4eb+B^*GC@M+zDxbXOrJ$4+50B~7MRc; z>`}jBZ*k+rye{3<1amTfAS}P+y(fn0u@ss;2$E$!wM_z+fJY5==LyraMe1SB#a0kvRJaDZPP^`BInXIIyi;uM%oqm6T7$@W@o*WMuI{~niNU*{3(F1a^AlI_W^PzXP2j>JuX zS&fi3<;}hSx1vn%Oi}%L!-_xygppy+LNZTa@{M_$Z;bF7C2u7re=sg$@mRop)b{VO z4DLFJK<$H?KY&O=M)cI7i%lQL;|frfYSe4XI+hTmxtDUJqM6xFEa>vc-Ble`f))xl zZJkT6qp?%)o}t_F(Vpqbeqat{lr4@c-DanZzqhAHHM1nZ1>0uc=JIff18vePo(OXC zwvSO^;T9x*K-MC-f8Lb%0xW@Q@r&aeoEEt>#`KRM&>!?zTdFOr=R!2bVFBvRWRXo^ zgIO_HcfeY{wE<_>L#tZ>M#q~+4Z^FG(G4>)A}7cck*;ibb)X-DiW^t5DeTg=vKtY( z#UG;Z)BUkL-3w(Lpb;_~ahBBEIJk6~__3)YBhYJ^-S0BLf3~O;lEL+>4Wc4}r%1X| zxXl4isN-eaP);O&TJ6B(hgh}%$S*}{faK>2s)C)F?7*6^Ggw}<%|0$fSG_dozNk}> z@T1OAkbtN+OwLzX$d*+#$ZuZoPiawv>n6qdpl~s96Hggu|C7woR;^L8(Nb_hi|*mK z<8DX(3Pnc^e@n20L2H!&_q^OUWsc|RPafD6?-3jdswfh^vq?PJH;df%C$k!IA4*v- zB!`{g6B6&PpNcrj)TBj6lw}sj#ixv%Z8A1~GHL3=9B=3}GZrGJ=MagHh5vwv%2}Ss z&&JH|GkqY5%eB(9w15w^Nbe`>A7t9}^`b~RqL2Qwf0z|sV^2xs$#+^ca6@`?T`5QD zX2LlU4a)geSWDGH<7XDvlV`wK7-ve5HUP>)d#CYzrHrbbwi0m#j*NO>2Qm2l;2W{e z&C6~mAC0y4=sm{i;~h+)!pr}SfYqGNt$G1md%GmaOT!SEPsbw)IC*}Q{^sYi)eF|( zQg7Ulf93{lF^Uqtzz-iWG&W>h_GsI6aVK1%{e&op%SPq@hE~|b%43O9e{0lX{gCFMdcKT80zfPDU3gNSu@-+$PmoI z;eBuMm9fomPTIlap*_J%cOlvU(IEXJ+_*Uae^5)qZ@~Ci6aiYGlt^5|92JZxchRIn z*?!}WQRiPGM0}gxOI?C5gE`eMO0MS)!^*4ZvVlc$O^e+z*% z=4?K^)pSI5yTC^O%qd1#3ow?OJT0{0^ zPYjqp)Lk7OVGl3IMRG;FQd|7ze<1RuaqfME>WRBXis2rd$(;%?_EPM(_|>916+d~0 z)9}Y^)4xGWFiHy@Bo#g$$W>tshoKQldX+>=CQpu22B4@I|e>8>hd@r;IkGni~5##+yAaH!49Ja$)6plroU1Z)Bq?m=@bPw=;Xb#!1ilY|EZxbziiET#XZBAWZIZ4?(DO!s+d zp8H;>q@0hQb&&>2&sqdZ`UW=oXYt&ql7@~D4cQ(__}(Uf6)llBfAtQm?#7h$zl~AU z$!)D1MQJP01rtGDVh5@{zq(3Zu87z+=BH$waMV3rYIMG>vp1(@y&QG|TC_p3YlUx| zbhv+$*^DXTa7TR0YE*9+D9+Yl z<6<0ODr**0yiH=9f65VHny8}W7INHdcXy#Ev_ldzKbMTMbN^m_pAOIlq4G)|xq06q zirClw4!B2uC;sjB$;%7Qm`45@Zv;M-yGfcm3FWm8CDyI0AsV796h^om)nQ-=YYWN9 zKD4bsx>)~2y5C3Hlf;N`WI{j^en)PKNRgh1#Ma?g-Jd&!PWK&qwJPZ8HAvr>=u5vKV|;5`u7qI~JhcY(|g>=v&X>F^cU zTY*x`SG~p&e{~bld-Q0^K61!b&}HhZ#`Y$er?Uc|UVDBd2B#*%)%qxPX5ebCS(4U3)|l%_?gxuE)!C)v@kk!s5YZ%&4OHIa$jT*B>WN~!U=LHUO$HtcH^J}SO6y9E z8J)WDwz^g<9JSE+w1+k=m#ybZLu21*H5yicdiM!3oHY2r@rjF_MdS9r>bdxZjmEq-z5lIF$jk?e4&PankM z-bFq`1XlS3+sAA?Fetb=_Ep)C>y_YI|(opEeee^CY-z!;ozr}wk;J58&R>c_Rz{m~4~p5o7y z*}9Qs(|4kjb$EW(4#&7?UZMR?5cE524#7dU%jVGaL5fkj!(;~ReOPHO{{i)mpV^)Z z=iF^9m|4{{(HsJCQ_})=MPo}+ep(g8}8LabUjj~Hu?F;I3aC+xzV+mL3kw^76I zz4nn;H-3q$z6S;SJ9YEB?e^k_xn|}W?EX7nJ+6&*P^*J4pJ*qSb1TKEJ44;D!b@VR z#ufvw7_t@q)6C)iz)@iQ1|XC&{h~#%3{( z-DjGfF01^9y;&12p5$^hbC!;cz4cubWvpXPvJ+^UWK^isrxhT^dDTPFLwy3xWvgBj z-6O9keu)63k9#u!9w`DgwfQ@4ZgT&by(SD|dL#pp5cwO`oE%R&OHZdie*sXD*sBbC zls@Q#gCxq4^tvjdK?y}75|HmX1ju778!Ot|(B0sJBYeZEG&J0T5%WnNrx>v@fwwdG zC=Gh+;e4d@m!l@;Zw~8`Rs3FbgiDa|*Al=_R#MH&Ud&qSoPM5s`=YMsdGZgH%9q`R z^YVMx8X$0ga=}=`lp*vZfBi3+L=}lZQ!3JK$w~0|v=>cO`uA;~SVICKotk?2i*AV- zYW-&OZ5os4F;2;ZRz=3q{_QH1pQ>1}S5Fi_SpDUrCTmkinla*h=KE^>7>=Z~pQiHdivhczTkqt}Pgt!o=M1f^FwH4hG@R z$9XY=rRUFG_pvo}f2Q-fbdFNtHRFqMwC6J#qkX&hn2y~#HU8Slah(n7%0XWQtVa~J z8kyB_{~xPIdJ=|xhh~OSeo$KrJFj&Npe<_b){aSdj2Csy#L4NKdE-j{1FSR(;*N<2o1xzlWhONNcj0!06t;B`YI6Df3{Pg_*06sw%YrNEv?*} zG6p&7)*(PJ!8z_f7_)eMrZYDaH{t_Qn26Hni5w^fEGP@li-W3x&=%L$A=4L3Bd zy6PFH@zg{>vGNbKU+4TmD->qmc<&i7Tr1yvPtB|AMj7I$dqftqqZES8*#NqIUjeJj zh8iOVf_yffe~GJyznRXA7qWwq4Z~rO*zngp#vj`lSeFf$OG*A_wyYIsu5#J#;O1kE z>9)?L8p1m-gV*_Eg~p6$>~!qt^D&#+pz|z&^VHlhI6fQZH%XnejI;|7T@0#blO<9s zv{%s=h@P#8tj9`Q^i1ucW~iJkYKl3V{xY8IGf6v?H@pPFWpZsIlUUIvO1qg2(WoF7Jdv-!2$oi0M3g$54)oXF?dQlGt*L z@r`*!^Gh;^ULyda7+((hHxiT+I&SoY4U(@my#yy*$B8^O=|E*WIN5?R4}{!*aZiU2 z_ZEMi(wM-%>M>ewOU+5WH8Er4Ne^?G69=*Oe`7%~QT1(nGy{8;Ted+S$IU2cDCCGl z+x1m8-vKWaxalTsI7LwYRKBy2&gL!cO>>X(e8m?b@-1bg{K&L0dw82praYX8{Qw-& zJ3n=T$bd?;f@5l`qUDZ6gZqKJ(Skw3#BZtqZ)GRW)00uk6jk?XGpTfZO3U7@QwRi3 ze>log!1pYnaxNYa2~N**wb$_tve5Ot62?QERjfr}HI45hcDfvG22g>0;1Fy&Y6koku$pcgs`YsXkWa63cc=4n<^1mUI zfB-01dhc7$2^SU-439e9s_weaDZRX8fm+bE%HJEjlcFi*_R~sQ^n_7DY^}0`7ic6^ zc25}bY1Yli*fETzT$(oRry^T#-VX}Q&N`1O5oDirT$C-RVp0wMS(*(&_nUF{e>0h1 zQ)i)yn2iL1Zh;5}DPs$(c8|$oK?)o(4aOJrpq;{Ip=nX;oe#{Q!G2ZF4TU$;L_3xX zdGTbw=BQLphFemTXT5ViSR&=sZ>bScl`{)P7Ayl)+rC8lcLeNX>vJpW@uEKG5xoFI zr}Vbd+Ik;;i@;P};`v`3G&T%jf6iSHh8Ww*>8kExg)5YwE;GjWS0+e0PLI&rcX?Qo zY3sPb9}nA7@nJHtI(#`z)b0i6<%H0yUEnAx>aCV~d_A-aL$O+nnUhDuoBt;Q75(bt zRmR2@8*gOJ73U<4uH44mHrccRDs|}>;@}%@U-gyEQ=XS+QGqf3#vIlN>V(`OpO&AOc9`KgMhX zW@cXUEPTII^GNPpUN5Gve-wg+ybLTw^SO^@b~6P(!}bVxnYw!O)ZZlFyF^0C2)4h1*1BhJ~nR|Lod zPjNpqj?H~bpQ};b$Y{ePp=Q}w4pSU>B^nM)3#HvlN+Dvi4_Yhqe;x&zzsb7tUV5^O zyudmJ$^&3UL@%DU?F%RI#Y)f_T;hqYfw@1HJ1GybAdfkYs-n(%;_zs&ZfDEIoq7cc zgu`k#uXiJF`w;uy#f9i~ViKFKjnMwI@sBpnfrw4Lgb@ssj`3>ShcL9!m&WKOPE4*WI zfhZF0g)ySmX^)3HnXicL$=BA6v<0m@FJ)Y2ezvHA41=`w*#ghGiQGzqzTme0-$L6A-&7^FZm#MZP# zLLvDr23d-}f2a(F;R;C?uannci2QCpnJ*EHAWN|OzXuqYK(XQ{X&FjZTVxfGnp~uI zl!Pemrf^R(y0>dsP=jeI2FUDIkrTPSuiw^fU=Xh-MjbFucsTmN!$+}j-ADaCiunV3 zPf|yclbM}ij#t~JBxJGhh)3E^KN-DI!JkBCk(hrKAsOW5l7T+B8r7I;kAwiwqjWRU*q} zdc|y@tcDq?QS%5!nTZG|G&N%399|Im>5@80Hl=Rqi53kR4L0XB{ZS}_FufrFyqH8~ z7BdNUe?C&-2^wUNz}tu&kc-9tBnp%a1f{b>2eeig0c#U9&&_%&lwi>&On#^MQtgv3 zGBnLd=h`lko}sV}*I$vANz_v`;U|?G5o?!RSfqZ0L;@Kr{v?Mas~IELiuaI_jXv6)(xf71kiY5RI6n$Bdj?ZzLAQt9@O`Z+QC zW@)}6ARrtQ0?ekjH@lw-|KUhYJ{I<2ry^W8^ZGTpK3yyu79{q3{Z9Qgpi_#IsNF!v z-9V^0eeK%X^8~`!E!;Z_oM}Tb zff&D$D*7LtXgCz*Q-f_IHwNTB^zctr9i?G@wU5&B?>XI!v2cS*_j?NWLU zV%DU$27h}T`X|r>HSJD5rl31#JiU~g!~boSYW)&nI~)obB5MUJ=_pX)#;$L(RB=0% zzUp4t7v;)CYPO9}6nsly&!BaErZ0CLZv5jV2dl3nli5rduK^ z37Y7HZ4b_y-1p7m_>@*d?h+1le+E*7zk|BKn)NJZ7|U1P6)!ANSj&s_9Wm z0SZj%tK=nnjk|$!s}0@zu$w$r(76jF%$Zt6hnQw@^Grct1DN}>sZxC6e=-;|KbH@5 zt=MGdTKpRE_5o&p5my~XH%ws{zh61=B$c0y!e2K$$5(^&211P z5om5~TS4eAeg4=O`)Esjohi#s)!|yV z7Ryq$;j*gzHWnllUwhQ`@-WE_792On+$IGZizDAO_1jrY#q6XMbN@vJoirTcpSee_ z@Ic1ZW-DBvnk64u{#d8$O0*AQ=3UN>+RPWv+?*hGRLMNrid&yTe@8v?hxyL>$V6yG zLYQDA6#Ht&2-_mArZq48a?+}A$&- z+Do2&gP$8P&&_Onf3%jE&+N;au>_9@8av}8BYR=f<;v$WP_Z#FU}n zqBe=S9a9jPug1t2v&vo8aFcI+MAUHBiGq|{n>s~NYWU%Re^}QnO|=*zbyiU9IG_t# zYOj)4EM3TH@^a)^yF`cX@LYhMLN=8p;nkPWHIm+sjc{ieny=!_fBDkEsP|C%Wu;z( z&D445sLa4f%c#CYG;C~mGwxqB6k!05@PTtr&atNx!J0fH{NGpo9gvuvmeAMLpVM>E zM;%)2SDDU|e}322BUoZ6m-M8neB$VZWgqOjA!RA&hJ;?NEac6o<+95?HfdTqtZE&o zdKr$b@CQN*Bw-nUx-na-;n8V-1Wz2(iEn^7Eb2e8qP#s`*X zSR`G$71|H9wT%LrYjc)7ScV)IGRMe;ShHG$NU({!{;s(V3(iEHy7{ z)9G|24V@DFX+*x%&)fzii1{rB1l-YvhC<%XkwObSIY>XKMa*9pg^Gy8{Ivr7YYS=MR+)Y+eK}?UhMuWs_ zRO1oCZuQ`ml_;&UcaQmP4D;9?l>vbF7ac=je46eh%fz!Qtpe?$RreYcR7T)Vjp^yo z%50Y_&8TR5kgVuPy%(jPds#R21x2Ig?H{-{e=zch`z`nPi#OS0`JL<*8#g1nufFPO ze`spO$xi$*F8iDtC@|2?SVNQES}=l}RHliz6Ne)q>P=pmWn`VhM&3f-NjU0pOy?Na z)7JF(4m&`1@+`4;+#oW0OhWGAXiXCA9}OSZHmNCrV{z7vsnP<8*Y}YS@;VVNcl25 z-r$f&-JmGii&t?Wj5coSXpHIo-8v9Vi~yX4N?0j^{babfo@r9r(2ewQ2Ij~na%dpk zUt#vxhKLTZpDI3R3|U|5=KhsdXK{oeooH|54*?F?Vo%q$SM8}7=+vw}Op6o{e=3`T z3~C&=>}TO!jY63sb-5DhGzI>LVB2L>3&A1U9qyu$-jfv%5LcJ z8l14WguATDV3)5kprxVVRy$G%Ai+FNPvQdeT8bH92y zNpZ^p9#Hd=SzQ0_U)t_XkARksRBeG)>Rs!JmWv)MdW$by^uYwMb3hrM%YW5>1}Y@i zv>WAtybk4c6AKr~IpqRHNyN$C#T0V}WjCnEU{Hf|HKL-BPa!}I3NLV2fBQE5N;*S- z;Ad1D8{#PY>lnit|#k^V8V!q9OhpM8M6R|K%OV6AN&O{)8Mtf2=ARMb~$QJYqA}uEa3Zn;t!<_G%8NgzfQtui+b+|EC6SI*83*ju9QUqQEt~yQ7kEWFX?s84C&Ys5BRZ)Y>}$z`S7+#! z2t#mUxjaM3f28O}A!wdp^Uihik_WZ}-TM&@lbW?T9O@7D*QN?E4=1f$k$g_L>_}Su z)&#PX(ldtCHbLo}aRUrxEJSU2wNmXb_pl4@*?Eel*9mX+9v9ixp-^!K)hW(QY)65M z-p`P;y{KaJL^&rzDj^R8wB9L9Su3)bmw0boUxHbje_L1@A=qjOvl)lxlDhk(J{K)=8&u_A}xz5OPhhx-IG+KpLFt`iuU5&b^ag%ea=JPK<47o#7mOSWb zd)MRfops0Qrw|be$=M&lq&PR(CN15yR*K zlOe6)e@ej9vEE-AK~nlDr-14hhl{Zb3%hdinO4jf4U9@#zTN3PYe<0~*2uXS&63YT zR4JwZ0Xvgkd%^ESN2?Br&qG@RmW-&fMqUAnEun;13?@Q`-NRHV1x#R2DD{Yf3+2Yt zGvQR(_l4WIB&1@RfP?+-^P!e4`lek~gpOXpe><(q&!ZCE>m81F%GY_+OZM5Sj#qtLJIkQsX4F8M^UQUiB*!eu!!STK%ocZ*`UJ_I2Cs`Kx ze?x%x&SNf@fl0L)f;GsBy+VgF>5z^ng@cJsR+?qXteJ#0zKapQUd8b&Ym9P_3x@zC zRzD%_Z51T!>%MbKjEXp8*`DS)Z$4VPE0+w3$~XREI}hJa>7#MS7f+d7ENXs2d&n== z5CN`zV*F@mDO;3?^l%k_h2_%i;kUlre2*e4sfzspK zMrwUQaiv1Kpq5o{W(ZI4qki^Fe@^z&H6)qPJG>RCwtm6ey2k^77>NtJ4M41<=}=u% zBbk7Vo$OEM%pF@ooq(xA|Jy7}>-Rj(2OugsyDqlXp>7PnPoe}k{u8%%&!NZ{wc zbsxFIVqn#00yRp^QUC(bZ*LTby)heQ_Hyn3`oX6TP65bsiEI=sY>~0r$VnDZ_>--z z$pMAuwpFM?N^tXRr;vQ3fAma~IKP*2En}C}h{&h~hy|nZVaaxI30y>AB5(6)Sw$FC z1({ScfYNw5;sxCV3Uxwidq-KN_J@T{87Et&G1W5aOoReE!S|I9%-7UJh=Bvx(O@%I zbMu(<;@dveBn4hJ5j^D%Z6-UPiPmU&%4_HL_x!|x?eU~(D<72ae-$KQ98c%FfkVIoniE-g!bFCZys(0c4L&{y8V+AeB%U&jK8+ zs9@t*Y?zR3b}fHTXQ<5hZUgrc#Wsi&GiRbhbV{El*~;khFZe6QyYh!!U^}whb*w81 znw&ZM3{IM80ZyoQ1Bye)m%)kx69F`rF@^&c5IHvrFHB`_XLM*XAT>ENF_-b70~7)_ zIFqm(Du0c41ys|0+dqQT5D5`!rUEz3Mt64#jFQ~OfDzl+Mu#9>N_Q!#BHbdOq<|=? zA}JxEqI3wnL%pBpxu5_4ocBFDXZv1#UEk~T`JMf^uir8iQgKAX5SnNdUI;7lKwdZ> z5G*4jCHN~GsN#XZBH>UJ&;W{eL3j`a~5%KZy5r%r; zgn!XkXGK0ipbrx70yIJ35Lj=7Bk+7=pdr))@nOfN+eV`Erf%@&N|Jy+j_~&wfU}5mT(*21(PlQDM3WmbrXb%h& z<%dK$1D%lW2%wRszA)YwF9?L99M2u0?teHmAs*@tMY=;_1czU(LxGwq#y}`x!awuj z;8-LEj}yir-OpzfIZs2lW(}018rs7Hfx_be=l!W8u?RTf-u*=WEY}T%_CW>wEjb}k zj!x%eIC^13%uq;AFNBWzA0L7W@NbD!WoGI{5v~A zjd1!c6ZVfq`U0&%1o(l0p!3haPPPPuIigYSe*bp=wPF#KTdJ1YdIEn&{+Cfz73~WQ z5E2sw3W-X9fMAfgG*F6g2>NH9TYpgGpFaL0RtM#T2A(H7@0V~E-VJ%g+1T;s3EiJ&^8x ze>@1#^}-WqV1Oo&0rlUm7Kq>6H9$Bby*&Qgs)L6T=%9jfCO}#UEG`TZ|9`DV;xv)I z2*+DUJly3sY<}y_&I#v^L?LdWame#W3?T{x`Y#)SWpFpbV+2RQ=5G@eN1z=3*CNkl z1c6ll>_Y^y@6i~wUr091%bn7DtoflEtZC=kVW{ePkU#rwJ7B4fv))g9 zPl!`<)tM8PIC-{rVLT;ZpY6sZO*7F0i*!{E*)nVo(Jl5k+D=L(M}OxYQ*N5TziPYQ zr}*$Nlxs@qMzk5WcUG6@>NB#*n1A_Xf^UKKLzX3WnEopGC$<78b3% znRjI&wO`IY{lxC38x^7FH#OchGXK*{FGI)rO>a1_ph2?2X~oi)pa$;e3EobT#I1i- zr_(KI2r%aItp_=UJ?!Oj&zs)kjTpBOwU*9wX@e-1PRoS1hJRSniHT8rBQq<_E{imL z?jtX;+6(X(KYh5}-mc0sHi`~(?92LZgjzr~#g-?!l zLK$E8qT7hX0Ds=)q47r>{wEv6*+4@8rrw|Q@vW(NL?~grf~@py7H0t9`#)YiBD-^Vghb3o!_aAJbfKm-c zh})}@Ev{c7rJp|T9XUh=n`PR7*k6y)S-UpL>M4s~q2-WVe1DE_br zQbO}VLRN6`$eZCNT<<>r^rW1C|C_7imhn~e{?u;3YsJQ6b3Q$aP|seNnEXM4fK=y&|J`EpQr zVHAF4%JN}Y>kX-pEl+&-ywg!oC;iGuDqnS^^hHyXG0QZ+YxMHDxO!37G$XsVg|Jpy z?#@g1&GK3!<^vSR6GRFtsvfC%py!rB!`u}?4=^9EIerdsVH8)4rH>`I^HDGTu78X$ z-f_I#O|lL}I$Dj`%`0~_C{bW8A;sj_YC~0fT2$B($HyFfJG+-iMs7USgRiaF0{}aU zKVSWXphpyn`Pr^CzZyL9D-QW^6LW=(E6Z${4O$vf8vLrgn!LEHr$95wS^<03wz?*X9}C_ud!Lry!}2_x!Cc) z(AN7<6KJ#Z-YUlDN&}0*Mv<5xe?xbyw?vTe;}V(DYt;oeB%y8fbe9oUYuQ1GB$AIr zgY?2c-|Wq+8Hb<6lYbl%DU<9B9*hBPxv3^c_nx|cQ}jumwOp6~q-~W@-G6)0*PH(N zm7tI%O(CwF8#yqB>%0yAZtBJ+FuYt{-{m#e;^yu=H>xE=<~{MJV`SooSvzxe(`T+K z{5cNJqfy3>YicQZOFzpEhE^L>n{kG#VPLDTl>lx)k-y~*y3~kd5NkY|*1U0mhd(0@ z6Aw$3hM5wuM6q8p+gj~+DkI40MxB3*C@f~*5-*k)lzSb5X7-a3+(eQqd8#CKFTENq zKWvzIPZ?SxG^cN6e)}=(i@0UJ^$^Hw$dyr1W8AGar1rXHPp5ujd*i`_z9h4Z>b;yr zph1-T5k#A+L=JiHlIVIGr1dju7J<4z%S2R@r7Pt+4vHgwB5$R`R5= zq-sr=ft4E~g+*kD<@!c+F08-N^iSm~Z4WVfFQP|G;{MJ_(${moke$*GQ0o&^b5~ni zq>i8a=p!u6Ev4$GRgE9o_idwV<+lvqqFU4EnItxT4Sc9;O0D_kt9Nx9nK=&01UUOy z-JlVL){OqsLr$4BA@mvVoOFNqXRMu=6^IS!8O6rDY59j3|2u!zn-*Pp>g&?0 zaa>ztfm`|4n>1tR28LprPg&Nc^i$;Q?*JT)({**Y8e7x5tonK1GckIYc&=XAFVeMU zN)C3Kq$QCNOn)L-_vxBF_^4ep)N)pEvO%DZGRz3LtMPi3c~hch5LHgJrM85*4%(>1;5Lu-o(H-Okz`w@1)w zTsFm1mzcpNzPwgjP>#Ec?vbGF^VL^>-Jy~h+j!@s$FP+BtqBECKkjR;Q?Kues5)=@ z-62$A?bKxMN92EW!s9n1kK%FET_4&K!af}dV`nL6_?aF@rAL3cX{j&;&g z2y&SqC_Tp2tx29HbS7Ye2ftT-kD(&7oK8_Wk@isW|FHWx_o8FFq1=t7tY9Xxk>v7r z%v{@E#?@wKHLcRiAt{D$3##vR&jrc>^~k?T(sruM?T`04I4rfh%8F1CUn9C*cR%Fp z)U1@7|44t5)#m+nqw2~+%2bm4!20s7crFZy>(h|V-Zh^te1rh9d=^<*towL8zv<^V z@VQw@&)|35s#R;AYedjPfXz$1w%R)R=D;CGmIy$7VHx;rfHDAy=w7^(MN%REpo+D9 z{mn_>OW}YFcy*Nf0q9;)G;bN>)Eza7Oo4B&*_waZKHP3T#c%<6e$Yb8XzIB%9w%#R zCwLI?&0U)<79*{|&zUSEbKJQ=+`ewtPt>wxLuA%I+Ybvp3vvtAA5s4Nwc;i9pq4_n z-4mDh>wIoiIW0&^Y1mlOfilkS+JUdunQ&4h16X zZXR1#B3r=AuB}*sSe?2xG*dS&SGE7X@7;s&AP{h>>nb?&o z&+Pi~-Oa^qHPRbT&b&L~ns;4K>4*}PG7FBT2UXa|npPx)%W<;pwtcK}+M<;a&uf+3 zJL+rU4D?HS+-1LnxqMpA##Im3ll z!tcNCC0YDZBCW+u7lRpYd$rNqWbi|FEbRz&@{$W~8nVGEas6>}ek&*~_S2Md#P#&X z;3aj+v76Fzm+m~+sz2MtU1Tq?Td!7dR{Q=isXlZ@>2P)9ojtU3f zhzhyY!OJ0|7Cpk{j=}p{ryz~Yg<(pf;#p2-L_Iyeohs!Ezc0~iJlc~B*xiM+h~K4+>@0YBzs!H|;pRB)JXyQj z#(cNqyzdJg7Y&8Ju(Ie9+hkiT5R%*(MBVyL=kD>>9MKu(%A3?BQ5UmL*L$oft1|!- z$#Aa|zZNr-P`7!KM8ZFkhJhH?C$B?0BEtjQEN_h$nm_O1UGtwES@#I=etxgZ5YJyQ z^dlQiOQfDvGsKciWiNk+cN?D-%E?<7XXh>}Yb4byKite>zH~72wU*@qiHcDHZ)GIz zl~WBj@ANTdSpB2+4N)G_Od(7xmELA>bqs8AN9?+Sm&Ug7T51Lb?}oa(T%Bum_dwIp z+y|ZILs1+9`@_b$W0iX6NUfCDhNgY7@LtjJpRMn(^um!di&B3RYWM8aR0|j4R;w8l zX0VAP4(VGAx$d4%c|~n!Ip_1f@?&t9*bb>RO7`5{%3#VUV}b1zPBsZa$G_S%}Y1!)VC1`wZmek1%S$>8Vl*x+=O9bavNb%5?i2zQ0PfX)&^SGqgn3^CDw!zl)#V zO#P{*oV?Xp!P|(R!Ciq{PnzZ^$LX%@Kt!+|C#9TKn=0L~^|rpE`r{?{3T}7*9Z)tj z8233v{g#@cxSmbp%+`vM#?2olsL}nj8|w>hH)jfVl9f#hj{K@CEpU#5RwW4Jj`iwHc<+<{en;>e=9gx8Of z9;pFk3`7UM8C!2R;^3R}e7j7mnTDrKH!2`5Qt+)UY3(76o1%VSYQNH>aF#-QB9Kuv zY9zB9sS-5A^uUH{!OxCHP{G$e{>2kB(r5QO=N?EJQAD|_PAy6+MJR=TUREc4mOzxm zW_^Ee$4@==LkGEsMXCMGWLO;ZEIrVNk50#7p5|#65(*83_Truk3G3!TFgL{dRg`&F z{FI-6W&AXDSA>i#g19psEwXnXKf*%gOO>|0D=@^!FV(oI{l@;lSbm2I?2R)DX$gZ| zsQc(PA(7!|i91TyzE>0G=w^YDTK4oa8I^zU<5=*eQrHp7^^U&yQ|k>wP_okcRD#Hn9hDWv1M0JO<5v z2}M?F#i^9A!^P&(1OZAFpR0OBfc@t{wx>zCltmhnOYH6z*eH22@zVo?0eco(NOXTz z@L!qURqV$~#(n_T`G~jq#S5u-UQlHQ3RT1PX)lKF)I%=P6@2p#?x&+8&OKw*KVpfL z8ZUXOC2+fQus}7wW-hj4U7?wRpJyl(BK1T%_)b*f&CJpH-VXP)m(rY98#!xxo;SPb z9ygV~C}exc>zZ%|Tjpm}2*k&;3c`O!Q$X}RAor%t>iX9>dGl5uqKZE7XzKn0PJYh& zn#^Ha8A9b4kkD$}NjV0-G1o~*a5_~4Fyi?9-?enx0w zamv&|?Gn*lDNh6>yO^;|9#%qy!Qg9~W=E^>5DO!RNVd`M;=)G0T)xS^pXq;o+&i#r zW-hE4be7iBVzB=Ps=FRKiQe%qwFvt@G>DY#BeQ4|*t(dZSJ&3@a}sjV=kp_+XB4EO z-Q}(%c4~b~KZQru!Ya1AEh`DQ==`|2LElj^J|>D-k35H3`B-ei=9{UZtn`B-#=o2| zu9ePFm?_QjEsdMNxJQns(D8pwA(^X+v69q_-F?A)nUqG!fG3aoEPW(ii`*Do2z1LT z=S47ci7?ITK55sYYMxKw=-Yjf&jvEsMc;J6j6l}7ySJ7v2$>Y}DwgmMA5o5$f9V&M zh<0gP-E0li5ti_J;b45LXn*o9pTmzw8OFwajcdF=$@j%mB$(3b?8<+_X2b6?7|d0n zquD7WwD=vAUYlqdlvB@lg`7?dIWOK@$8ib^;g_RIMY^)2Os zb~*;xvT|k)cgeS9a_4{fIQKxaYeu%^C9P5M8rRMKSc>cFvgJf^bVL!{e8l7oP^QHk z1K*UB{=OUb`WZ(`Y%d+@`Hy6yCPAB`wo<;lUs}a6RF$`++xXdpFM3O`=i>%p^1Juy zZ1s&JL7V&sje1|x-U&1*DKA&rUbqV7jK1_)-4KOc<=-6ItMo6dbU6t7a9|zEQ`*B5)p@ou z{?k*qGH&32y<_HWNz}FO;gXF$_f&IVv0X^s<4b$o`Y?YMGMK6VSiL-2HgUrAIVw2( z8qz25maxIZaom4ZH5Ta89G=(6hIga2QQqW+0RbD_Q$p79i}^yC0tGGf!f0i-scijw zRl~vR(QIRwuXsL{g7htNuf~Rg+)-s>@iFu+YejeYI>X??Ln;bjnW7dE%nY(pKi_L^ zcv${vOfz%j_UfJ&J9Dpo^^5^=k|@`rynybI6-mg^1i62%RRAXw`HjT7NE4^msxhWj zV&lvEIQ8V?>YS36*dM*ra}v6Cm0?8d1#*1J$@lv*=SImccoiG!mZna7xV}u@g)$P} zj=QznWqR5yUzGu*_WSVKpBe&V-X>V9-!HolryqPOuj=%14mAKAq@Q=k3O&6l>My{{i22UsIRCiUJb?G&PqnXaf|t>WTtb8v-;p zmoR7p6qo9X0uKT-GMDk80~7-`I5?MprUEN}2Q=Gn`?pydTCKgsDr&`^RWnA>+KQG) zf*1)AyV_ccJz)-euFbatg783!<1J(5Ow55R{kc0>bBo5%^HAY~Z;eXWtUQ;;Q9f3m1|1SrB zH8dQ8!Ryo^7`&ez3JKKla0ZG=0L7%`#bo3`AfPx1B=;{z6j~mr4)H|5fO;Z89TXDo z4&YTox%!|H_6`_)nSULD{7?a)n4Fx9(C=`diVGZ#fI^T!JqX4D?t-rfg*XF^P*4OM zZAK`(7!O=i`aiEd5F7U1^9Qnst_m6`R@bBFK z#YDvZo$hb+uS5vs?_dZNigIyd!QY{84kP)))m2cV}yVZBX&LKdsiUAOUAMo#Z?oc$s72_`Aj&S}pqUf(Q_*>RM!qiYME^s8q9q_9^ zbp#p?#b3LR=%39xAyHmPzrTVV0tvJGH3ZDVRn!EDaPxp`tN(Sus{sGe*~2kFDUhs; zgsd14?goT=Lmfnar8o9*h5t5x{#N5B@b_~?xdQF*L%{tJc5wV3z|S4x2?t`(9&mrZ z|84lE1P~Jg!Vpjl&=ziwKmz`g9j}Jl{gLs@Mq)L+zKQGg5p17|KCvm9rAxe_^&Mg zFOdItLtqbQ=ig@j-}e8*3~@m?`}}plgVqCsCx9LbPXpwCyPCoOkX8>4LwLCSw^bVh z!4p9RX^#i9u$ZI>Nb-+=9^no~c*9}$5E!V#AISXCoBX1hGXe>}hjK^!dRy>OAkcr= z@ce>0;olE;JTCv3AntgwVScaimkh_#>7RXQAfYJOFW!kuNdqBhG{gshUp-!u!Xp7s zP#E0%H?o1EB1jYl9|FYB><_dNqw$r0;{jjwU-EBu z!{Od=C}4UL1yu-ktO{;Aex<_VCA>1ENV=1HY%VT5^x`2orb}bYdNJN8D|6-!4m{l{ z27c(+s;}yHoc-y;p5Nzcjv61yLtzf^x;=+&?BvBE;dp}I5i8duRXstTS+Xj-d=a{f z;2vAVwYQgV!|c0%jCiRY@l|z9C3xMSEe6Zzgf!{5ynR)isY}l)XS%gyh-t9xHC~b0 zXXMLSa)}9AjjIe)<~L z0rfQHL_(@PNH!&Boa?!LxF1Y$4s7_erQyT#zA=WQY0`zh&(0KYM;iGvxgR-{74JTI z+^=F3;h6doY9TCQmJ6Sx$?K%P=>-K*95rZ)r6_u5cfHt6Z3&S#ap1GM$iZAAx z*k(|+l(7?kmi$}5UGI}PvS#9dv@3`atQXE>1wdYZaupbJFzl@LV4k? z^`1mB<)t5VZo@egmLuy#UY6ADPv+5^&FTU{CDDC1i8c&&hZ{eV&+=$|XWYilVT+@l zz8(S{wMvyM18^B1Q;|B9P}@%#?GH=G$P-WoYw2WvsWm}-eU@T6st;Y4~NHI#K37^W2#DVBo2u!C}-O*26TV=654`{#)5he{ZnWOvDXQfQr_;a#cI> z2Y1DACCa7=`F+z%C^3xuAoI&+8}+J_&rv(PR3R$_LD1g42UZ0fhmY+YHY@000D+Uc zAB6^gB=T7el1esC!IWV!){5oWH**D)^YG$A; z{w|3{lTev?hHyq=*VcL0t6k^)X3y*MtSr%g8<6IgzQps_o1&sx9dnc4oQ4R;dkRD) z0`a$+vDvDR6U+FRNZsw%b5tk6gj}hd6d_T0-V?pv+4Tn58gqLa-2CHQIE+Jn9cJZU z5;@dcAz8UCm!hT4&B`$maXUJe>~d69Le%q#0p^!FtZiA6hffdmhM2a;cHGr(%_PTv zmm9$k`K)gx;~TAywonM6-+3p%BTOUi@1r2c(7EN>NlF@(A>$R1*Cyc0I%0WMP>i~r z2iJd%@RnNR^oSg*F%#QOms#n|BLCt}a-a)`;)$^ZMwr6zglty!fnCXEv$Z!LW+yBRp_i zO?`HuIZ7!ozMReMEJfLwPA9p!A)SY_x4U=2&{ThLYLKBGuPfiSqXRg97N|BB8Yyx! z-8#eCb!n}`O;)}}&*C)ul5yPeVY0O`vk^xu7hgq;;|f=V?zxi)Z7_3Q?YEVO|~l*<*uGZfWs+om8NojqV8i+P4Rb`PhfBCrLvh1^gWP&F4>?S8cwDwHfJfD z`eL^ltiNYndzkq=r`zd`+_$RJxC`)=1|D(cUN5H$f)El|2$TAE>Kr}zOCRWI;4JAl zaQo@pR^eFa4Os5`)SVkYXw0+kOQ(EU1)R0j&PTJ|=o*?i%I_ULAJCQJW%=Qt5$wny z*}cHs$YgS};G7SC_O5Zi&z>dyusS5WDu0{9f!lQ9ja7z(%eUQP?HDeiKw^Cr}E!A1umoo`l-+JQe8WVw@luK&1In-ttUFAz2G>1)I)p~^)|Y@DI&42#OH_Z zuFI(QNG(aMsQHH0z?yjT`=*5(=bRPaYMBH&&n@9a!jDbIs^4rB@!bBf$b)L*rZfGj zX8*&8Ddujf=Dvf$BPQn4I1#-jRwdM!P?ZUB0c{+m$}n z-VQ4e+@I=yoQ$C_$_t~`S}@y$~+hn*$^SSo(B9^tt9`-(-Hk#S^qrya^O1s|!oJgAX z{=v}a60>Tq<%$X0E_pkKQuFJ2ZQXSH8cJsg)B4tb0dk@#Vcnc`1QoO?cNfY9xYG2t zFF!@r>ZBx(?kTY3%gPB^EM{K`FPmwiqPwC}7!#--`yx^u!>66z(xC*^KAR9KI~wcl z3tHG_ZcSXFdy&3W9PAfJd-o0N<0RrHGmc}m^Mp1PZdKeEHO@q^zE4usswa<)gvg{J zu|s%&!iwPG%H(&$6{Ue5O3TS~v;MfXOS?HbXJTiAacr&(rU9!QKG4d!o@{cpgK*!I zwJL@gX6EN!CUBdSqAi@~r6&XzmD+D#q>zhM5!>zbBvKO_efo zdHTK2U$Ol;-CYXw)3fJ^Cmh)VomcppTg)ndYZku3s|wxd&!Cd^khV5C)ot;Y41zNLM~~B zg*#MW1TF9F%Gq1EqX{7!Ex=lw_bU8r_$F2$$9Sk-_oY?_Tp@>fw7Ev5E2?O9i$c;s{xuvvdcAJONk`BAM*&^(zUWMc z%-FOVoDoWftA_fw@DztEmxH~NL*ro9?Qt+czTu~k)wh0XdM8xhSG*U$z7_Z|?8p7$ z_`N|!N34IG+xwHU)|O4rRNs2e*|2nf?zrrs!4?J{u9Kg+9V+XiG=&*v1A><@V0$?y z_nc`_{la7yRhzPC0pl-^;LqgZr=djgp*L(>qp{3p`RRUfSQ8fB{38?BC#xlD8I-o? zn!bUGtD@urj9{3#;QNBht90=UHt^jTq4mMi=YigJYG@`7TG-ma4SLjiKT{GIkyi>VujzVlQ{Dw<;XGYs9}E4I3sn zd7HS^M)}@1=F@7AaIVfk)u$qVTPzI=eivcdKXlpBCyJ#PQ?!9NK=_qV`upq#OS4BO zgpt)X9Bh`ccelXN$to&rJR*^rpX%d$+PNV!qNPMNVfVn~_hPS8BP=>#!wp95A7Wkw zyaGF6t3jhQ0knoT!`DTEv?n!o`wYsC?M29t9gS+r_M9b8_WC>rFLX42Xx-Zrsv_7I z!J63^^T44|LD~2DIaJS{ZQ_#NRY|^MAbVLBNRv>^ERUMa&K3Jk6WqFceaQDroG8Y! zmnrSmmu6f6?UuJu$@&tjn!G7y!g85?lVRC~!>9$}fBLmIoLEEyhAXu_VlI7S+7(W= z(#9!SG9}_-{n6)RKu0xy-I@GcN1gGjSVXP_T@X!tl7zfTm{BUv{1uUBr*wMLaIrmPhUUfiXTU>KfMDO+R8YmO13k}5Gqg-rMHS?e0-@F&@1BE zk=;O|d@^vDq|aht-5B81f+BoHP!9V%^hUkyBr|s{3 zam^jdhJ90i66ih)iF;3OlIb2*eMXSY|8+FpObiiTCo<}dJEOZ^dyE(F)PV{`}?lQ0Q$`{tg|z)jqOMMPkq2Z z<|W~OXJ?6Q4XQeIM>M5=EmBw3BIJ-K)$=t93{9{cf|(nq7Kr_P12J7{7kRqe7iZ#| z%AW-(d>(7%r=O-(-SN0u7h~p0uMfS)6UtyqD03rhpoD6Vh^|Uv8Rs(i-T=~Cjx`UM zYhND?%H^udV9xONhV=H3J?X#Vt4O5Avdz7J{*{5ZHH^|xKQCn1p&lI_CoMf+q**$^ zW=&7V$SxiAA&{ikeeNeWr<-i`4b9q8!qoHAG18{4o;KY^?3#SwQkqi)u=%E`1M9L; z=AG@St^|pkz>^c?Yj6iR+GcA>(sfg*v3ZmJ!L9)7!{{c1IhC-2;zuR9-)Az$f7JJX zIDi3xnnK%F){Si?y&stvsvV1@g*p##Dsl}+ zFq^~_Jl!ut8er?tGGIyhfsGa1E!c2K&39DMbpJs@Tr;)7fk|19bXctYY+c)a+c9BA zawn;+A@S=x&7b}$18@qfbQPv6Vj~l_V!4R^E9^@HiJk#>RDR|bz49xvw`2EzpgOGY zj5;Z_EI~T@Lt94YUY(Cf8{R%_8fCL^G9hnVAYKeh8=SO0Pt5VN+@0zxJ3fe-Ca}t5 z9*|ekvjBL`wWr3sNUE70BvJuues1cgG$9nMlCwlWdGBMEFkP`T8$~G^xkhq9-WvRLW5hI|X}=zKZ0B)U1&S$Ya)J{>lkR8JmmEK9 z6=W&Q)rC7Aw9FK-qlW@@*NJ8r>%bZ|rnQ`c&+pzeuElZ?*^cF_hL~z*boJWrO<@M^ z2lw&0+e8Udgl?!<=&F;pKA_o>w3M#Ee)T1%P461UWN&6#-`|aiq z;`QvvSiHt0xN{f;&5AuMNR_&X6EG@5-I0(D^)^GbH(oE5>CfD#n9Pi-Y?Yt7Kc>_( zD08Iwpn2)IRK#WdEL>cF#ct9czPPH{PIYb3F6i?kj*6f2YYK{*Ivh_orfQ5pyyI>- z$hA+Ln|7PZ(1StWVsG}=29}TOuXNXk66QAN1N#sS4de`>6kE&>U}V%s-XxD@`5y_# z74i}nr_2s|=1_oMl2@eUm*B2OdD%oemY9u!!d}%~&5sQ^6V3{M+IYyJeeAy&BQL&O zgN@8F-V=$sjG0p4`S^L%_SL)BPFE{E0;?vO^su23fh(xB;xy7cXc>K0y?ZZsu||9G z8f}(u054QA*SMa={Y~Sf>VAneHghr!xLT>x$r#Rf)3CzQ4n2OZp7x0E;dF3%^TB)V z&m!NhkkeVTixIbfT7sHeI#KIYXDoRfgX)B?yiV}oXUcZx0&VBX~$1iljBrH-w+ZT3lwqXU?7;Wa^WL1kS8h5$x;3?eB^CGud-#o8@G#wvyW@ zJuYtNpE57jvhLmy4sPTuq^{KxW)6~Bc_z*_FzX_xii_bs6pH~2g4@TeQZyD$XunH4 z9_!t)*yIX-;J%X^^X`_03e}$K-2j{f`fm3YQGUySg%a2^S8{f7sNc@3Q_(!6*&N}6 z7I}^)`HEH@XibmU#6DWM&GOpC#Js{7o^!+BnmcM)J#1}4N!5PlMM3t4idwF|zGRcX z*c%0M2TcW%zB-bj8WlI{7sOcK#tHk!ks_C&OE1`e1&IP@;>MDH#wprNb7~iKWb_Yg zT|@RUoR)kSDeV7HqHfF%eyJ5@$VlBrEo8BrxThf5ft(Ri-U+|nqaw~9+c6%%7}rbw zjf@kgJ5E_ZS2<&K*FM1IZB#`M1WTB5h30{`xztxTJpx9RxrP*@Ng$vX>=@t$79f;C>`x|Xy#4H0sXjO1(+P`bl|D8mIgAWWlW69jA`KJ7$%SS7czMS zI*#_;^|aSzhhx%48BH7_J)%ZppwlzXhWA~59!*H9EQx|jZ%{vDdp>9Mx`%-NS)F~? zP-ba;XET9@W>-poKA*pNW=SG}#a=P}ma8q(l2|rw<$P9J}%F^x>Kg~8DXD3H&;){JHlq{x#dwXV)i9y zyDgDxk6*1Lo)f*J-zJ_nMhY-#e@N}nAauI!bIU#`q;_b(ae`5K?xtgc-1w3@^}tnA z)w-p)*||ko%kJb^>*W(bd_<0|qm^12rLUBQE*H(5ne=dG?dxC_!h^>D0l}(ddzZnA z0uur>IhQbK0~NPfrUFPH0X3H|Xag1yIW!6{Ol59obZ9alHaIykm+_$k6#_Xhld&8r ze~oknRMg$pzH|vlODIEw2n-$4-637VFbpun3@}3pNDBhe(y4%yG}7HjNK1DKf*{iI zjr!jE-uM3hYkl9EwSIHXv*YaN>}MYq25ns~8Ed!|L=g@{a`AEl#Q<^|8Y-dyAdsIM z2;{?OVbOyk9U*_g_$&qxR|FIe6Z;Pbe>qnO2#LbvK}eLJ1{?-Zb#nyp@&kB<#CV0p zfIt8r5GeXDN4TpPKpx}{wFYQ#161KK2m+r)4({yb3bnOEqK^6362K1T0Pu>63P1iG z4v=wzxI)1o7(fGrw1YUIP6UG-0lIK76oT~nM+$avJ0#LsjEBd=!-E^-gy4p|f7(iN zJO+3`k#>Nm5Cp{49bygmMHrw7a)SI}#*NPc(6fUg{@}WB8>9!w6#_s3j!-ZJhCoHQ z!K@*!0My|CT@`hJmNNwQ$65W4!(+f-cLU(%=KXiNzoLI7g2H|WgTP?8lQRhB1%=rH zY@m)1fR>^IkwzIsC311W=UG0f0~h|DuNgyF#6j z2yO(_@fSs&UujUctN^o?gF87vU`Pc1ulnSnt`IQl+P!%Gbk+d|_kel-wXuQ1tZjag zuy%9i(T71@+#o9Qf4QI_{J$|<2ofL&6cOeZ5dc7301!{G9nY`ydS1?uf8R#lUoeV+ zueUSY8DN7V0r7>}Ku~}9-UyI81b}pPgZO&?r{SMFd|qCFH57~lSV3%|F#Nx>qhN^5 zpFOJiu24^aDG;SSUI6gd@82tPl)S9rFh{Sy-GBF&$57i)NllgW595E^WM$!=0B_rDat{?~MH0REXw z6ONKC1i=2c%FTd+KrrgV`+wH@?~wnW#D8V^-&FqJ1u42YI{r4Z|F-`>W{?xq(d#b< zlxp3Os0q-3qvini->!y`KU1p#v4*-i{kK&G2|`VR49pg#W-eXtBP%Cny8}xw?Y9@KLQt?F0edyr>DahIsy#Hh_m421lYo04UDB02{a~ z{;xKp6v_i~bhZQie}exYd;lIR2=Z@0P!zy}8gQroKtupM&ZvgMt^W-Pq4FRcK?u8l zBYY?)B*^XWyg~q;=l{z44-Q~AS639t-_k=7{MY{XO9Fv-LcsVlQ*iK;VEfA8rn7Px zS`V&`VR5X38)a>ZcorA;=NDl7^jHq?&KII%03i(@7jGw`$5FLHasb7+iqPJVjs53 z&M)yEtmFlX!m1%nnk{YR1?lRPbfN}(t51>jR(*OKg2(sSvR0j;YlBzyZ}y_!TejItE~6#yQ!q!R!U^j zyVl*sOB{)3m6}I(>qooh43Av8GHdU5;qK+BE5*G?&50U_lOBpK{}F38RT2tYB=_VU zZk2M#@%JpF9@GnuI)9lr5F*R3#@nWG&}A^6c&jDrq&c z6<0art_BCk{cX>(=gCy+1B zr@Qi+f4{68`y<5L$3RV1s>8S&s+lI`r`bmAHjftYP@o-5gs3v?#5S0viFpQGLYH!o zI3F?~9rx4OfF^wmVt`)_Lh-lh*h#c%tzsiRh9c0~Vkr|(W>v>>1=dB2n0-FYLa~bG z#KPy4=@s8fa2>gJWn=T{)H*wG7931wD|Ck@f8Q&QCXQKx^y8Ri%KF8G-9pAT5Jc=$ zippI#1dr-o$i$|JpGSDUUXPD7s+sdD3{xvZe;EE|S*C<`M|!*Sfb@eGG28h^O+qPY z77dg7jFb!t+QmV0o+iTCQvW-BC#-%dR5prbJjN5+spFy?j@SojTN+Z^)IJd7Pi%|a ze=iEZMrJaCzk>(ZN&QJc5tX-7d+FrvpsWYMUeK|lq8(iCNM+MOJ57IP7Qp=!iAkee zb{63&nY+_H#gBhZ1ZnT96%~OIQJ?#S+1G2i?8NSu@Ecd1(}r2NZ|*0J7myu+w%y5S z2B^q4Z&SNJfvB!MPJp)T2`>kRq6W3Ce=qDlb26@?g^@96zFLL|#4prUe*7jq6{$N| zag-L#pCn;1`M74dV$Get`IZELUgoc6M)6oI>p|{Dakmoq_44L^uC6@qWkzGQ34hfn zrxuIUQH#9kv+YHvVL2)ixdHBtz41%M5#&;fpOA;ZqY`GTboYWmEfwQLG23{_e-O%p zP8^+?wAu!*@%HO`u*PsD(ScVbTeL<@xyPYq(Nc-@y`>*gRqxPs;2yrve`LDSm>p&K z+-!KIkgIB4(5=Ja!vx@Uy*J?v<3URAt-`3&oT@KTi|K=Om`VnDN6tatId=C#98$Wt zS%yRwO>J0J4(n5$PzrWW_g748fA4ud2F6+IyWkW#PCg0Q_0ZxvX#<*iwvo;V%e^h` zGi!7zJwp7a?GE{`F!pbp*Up?`6c@IeiJ99mUqco{-ifnD#u+knQ6df}g3C5B8qmD> z^{SM$9~zN4r^N?xb$2Ck03C7dRIqsXzQ^fXRMt$ez9hKzRS<4DfYEHBf5oEBCkH(g z(}C|D_A=hN7iSQ^d*SBR%$iIj4vM|cJ((DO?OEx&7yYr_VlnFbWrp75&;BJZqH$_0 zg$8zo?&8};rv&|r7S8RE^qAIm7D2BN#~WOxnjTYz**2sqscwk&xL(s&fA;0d@0kp_ zJklE)pE}Jod$_n=9WbCYf7X&jFBd#0t00BMq=woBB*T(M!I{IzIbwo}!Y{yJlhqUk zG+_fOwWyK)UJ-zj%~RJ;#nIgByhEQCWAX1>Jhxxg&9Gza+flX2LY(W`in#UIKeXSs zonKHGbXb(k%yE-;lZuK(Gm9vB4qrp(YE$dEsfqdtn=IogQtVREe@z_LyNkI$9reLz zI9s^HH`rn;5)wdsbL6(n(j^s>TnuKNYI@e!{h0M#3>tLLYxR_b;?P){T|i><)cmd_ zmKS%dxKcep9kwk{b?evIyFq#?a>*@WL3rUi>zk{la`QgRIg5d#T`2&k&)TH@p-lf2 zRvzOIkelC?j_#9-{%d0Gun+vpHY&I5 zx1hZ974GMj6X_}|Kb6Zqg>Dv$?`#=q4|+I$h+Rp4&7P-SJ@FH+xfkHh8P5zt>yRn8 zbP4Y+UR+I74)@L)+R-@V7Y&dLE0q$-=9ZBE_4z6BM{(o55tdO1dG|^xcMK8ALL59TXckEjMO>6-M0X zu8Dz1Ma>zse}?J8?Vr3TC||HXD;uI_`4_2JDf8w(A@%_*hvuG;I+A!Mxs4lTq zuv+QC%41$EIW~K3-s`TiD|RtRd$f4BXRLz^&%% zNx@ZGf9R_s9c3-BeeBxt8ExitbJ{r3QBU-sAXiXtRkBzbJqe{=AT#!^ z#^twXeyr)uKH4YA9|zCQFeT|QsmHFK2SmZ~91-6gznb?s-#wN_j%|rP72~wv?4*gw z!Vz2XMyg2^-At@Sl;VA2wbVAB6_=xw-}%Z{e-YknVS~n4^L^zGZn9_l-IN?oXaUcv zl%HJGg3JofnuS{q(o$=WRzwT^hO=7bgK7P9+LTESK0N=WJ`s1_F(2_pqKD#(BRKnd zG*S7~%@`9PX8dYi$>Y)abdGnth|q0U38&MYgh2AUx3Qcn?KhCxs9HI<7-wQJLX%hD ze^@y>>t`s0IH$k;X!vQVmbFLZZzG=VkSn+Zkcg{%(JwJ%%FPc+R4Uh-uOSXdx{f?N%EhxAst`R3BoOwLhy{dVyFDtkyx8l7qbJH(@0$2|p zJXaIL*}C6c%$9ABZqN&{S!a%F0vq<$e@ELIfj`Ws?HSfg6im|#Hj2dQk~a9Sje5<# zv8Qe!>aoqtRnCndIHqSt@U&`bPL50EbEL(INf!-LQfG2^5+eP|<(DrE{{ z3Jeh!`;vE|J}A?0MK$LVMnP9Do>+ULG_l^Uv#Z&reSQZfG9vpWwy`MU%1_@U@VYzP z#`}W1-b`gG=xr4@8zLIBeJF=~f2jBlH(qftgE+xHZb-Dd0>%9Mm+olI8|HT%<_FuR zTmz+zqJpvP^+!Tg#&QzFtk{ogYtv(NkD7|Q6t?u>tLqeLeq6OndTHXd?b+u!igd_Whu(_VG&(SyUDAl!mGd>(aV2rpR`jE)xkiQxbjjU^@{&v|3cizx?G= zu!Wx9Ba3BbU7m4CQN|Ode|sv^KI==IcXT2qi=0k_U<#HVP6U&?sz;t_eLgwDg)mB{Cbmq|Nk8?88yv1)q>34dC9T;^u2r=mYHo&Q`le>9bCyxS_=#%}#` z6l1x9Tux@hCI!#&P&q(@Add0Lm3isAW(v6?dOYYIO(rttU_XsdeUL8U|wNzW5L1~CbtYntxND}Ox3e^FZ%oz2}z5V|mzDY?SE z&5zQB&nPT;)@0VzR8?|c^hp`Mq-dcllTmjiKgPZAPJkXnKY0BHbYVxKRFMaz4)hp2 zEnRj3ksC)lx$8uYz*;v>oiY@1L(N-UjO-T;(Y;m%moURqr^juqnJeeWJ93=!Z;Vi-TUPvR2x^rCwb{tDnk_?f_=TeybHzij}BrL5nd zPv;={2E%6fat{1p+81hb?|M3?9K65yK~`A3 zHzmfmmMK?4Oo%?<%Gmz)L5|+&Md!I`ac^WcC?bM-e?aLvAeXc$zT%9RCqq@EDV}STTqY^7gQl{H8BGACNSksm%RAAS!@&V_%XrsLdOZtUcfi-QPT%V>^Mb0b zYHSY*f1G5y`Ruk+Xg12_;w?FdgN{2Kwp5aQzq<4+1kQdfk)n1O&vCP%1DI0An;lo= zsR>rWs)si@q187rYO8X_2}qw=#pqu= zL@)3e_z>8aRv47XJ~LGZ;B!1Q(%2@|;)(~Rf8gBSC#H^Y(4#W@>gIoXLnP@hmK%M8nlEk@--sWiorl9hzktw~yS{(sHASPGdtanH#yDYXl&T&M( ze<|JCCE?n0yWTJiZg?$IA$_AEuCGoLkcdS<@?#u_hv9wFwH;me!*WN30FOX$zrhzv zY%>A4oJU2T$wZIO^981o@B$YdNe00=$)bX1DgZveqqqGpt$i?IcLg+C$}TD2CQQ3N zR`L}~Dc8Qtgwm0hW#+<+eT8}Am`o-_>)C|hE`L8#T^Hg5mYbdyC%{3 z{U*x<>HfGfF%JckhM z%o9`nOV0?_+~wy(Z|(I|fC8D(y$L03^R5&V%l(<*C<5|J5zIsTJgnZ((XW1wM`G9u zOMm-U6-HPaC2bbj_^$_^WF&RwF%T$f)v7EINq)UcHKy+Ta&;PZ{eZ*GGC<$(I|$A% zaEU*J!Ehxgx*uxSaZb3prh|`_!tz@3eAshLl`mLNy=LL4tQ;OPKfN#Z3cbGY-GJn& z9Lr^UR*z|yQ_H5%2UTjtnPD>J&!&p9E`O5*a1(sPtMm1fsi^YyNsN6FUf-678Fx$9 zZpA+q^4)&GQ0XXFUUxx;W0^-L%cp4pv6bjkzJ-fXiHaBZ6a3U3GnB@iVunijX#Ox$KHN)27aG;}K0P$$yTU z;GFduLIl}JqKFzM#=Fd7u34)za~TtnjoLj`Ml<8f_SEmM^D3|0q<$r?hil?J-8hrluZt?XXn?$0>@3MW)qZ_br-K-vCE$syn_#3Z7r>nMtRDb1LgJ)BN*y=G6%ffKMuYK zAx$Y~q%2HSuu&*HHLuIBE>qo-7|n1&V5dsDiKy+3~Y8B7>>w(i?pCyJJ5%R%$#i*c`Vj0~a1i_D?2O zN@?uky-lcZTnH4e{UU1j0eC_u7%zIL{$;(qkYG9YIJ5exTo~>nM{Eh#>!>_5Rwj~% zAmd~j3ZVY*Z9Zf*_PH;Oqm?kyzL{L*R-D>oPI|mmq8d{RL)aXi_Gme?#`Dgn zWICfZ9yQ+C@)kwcI5xDn5g4_Y4Mu-v0d*+By!ukuZByr55`9xs8xz}F&@s)q0d?9# z2kx!{p59ag?p9DOX|2!nlLfF_5Ifxy(jI@Ue6=sV@=ij}edq));l@@Ja`K}J=L)$? z;v*-IN}|Grw6m?k6MyB^dyd&g=ACFw6Q%dGwrDOr=H|(r!%LT1o8KLj&<(8P$5q#~ zk*#v#+_3^GvZ`Z$VH~kds%qCQy zc^|wvV2H6J@8MmY(UIM#?mtOh=W%B_U6gyXQr4i}tY=gXJ(87gB{&1^0ZC1$X10-- zY-!g#w6yKwLc(3dv84J-H@y~9L(V`>Mv{upN?@Gy1WrckJw0g>y^oD89Z?2MO=M4x zPWYH0XI7jkSASjwN?H(?(ckoWfn=Cfbuu|DK zqqBwXrF>Xlb{$NM5C-1$T3hHrN>$I>i_aoe=i@pR{Vnb(-zhgzM}LCzz`_rlzUm}U z{&}A>Uk48uK9lb9H9#vwW88bgL8@OdQWsyjePC}or+>o=XM)2+ zu-l;YX%M+O@!^#_P=)3Qh;|UBI4c>w`b~3&hS8UJe`7}bzHb&+{m-hdzAk+&kC>vD z_pbWC_oZ^pb-w#Nuz%38lsZGQ8yNZ1VrFRx>!d+_U-xM;{p3V{PJ(#g$Kb42guPch zjDUne_J2I_<|Jj3QmLKW5>|RQ^kNm=+;^_VBjq2SbXY#|yz!rOmcRf@r{YT6e=u7+l+_7pRF`rRS&6ulzz?eZ8p67 zo%&Oa=eu>P@0qiO#8{%ho7S-K{(|jNhK_ckw11+}15T9~5j8WTiJ`KsQ}z1N+fjNi zTJbdnlVkMCafgH1xf}IM$8~HH^mE+J0h(91wi}0xR{9X6m+_V5vAL^EEzSgbigbAa zL#ER8==d%NT?kum@Mp*OONc9)4aS-`i9{MYOt-PtHuQOIO@J2{bWI-gCZn`EA1z)m zI)5t=s+=w{OD0~C3z2cMwKcT9GuJP3A2eb`|1?y(Rg*_o)cuki>|#zgs?6D)MU=Ub1N z_*gch%~IWVr&5pNgfEkw2gw+bwyVgWv42RmZWax4>Efnk&$stlZ%Lsu+m~e=6l(xq zN{-CfD`sJeD|+?53oY88Z8Ht#8=pIh7q$A>JXbUVcN#@mBbK+S&?q&&HDr$uTUKtI zgmBXPNMt0BE$iQ;d*TFKVvX!LJHC?c%llNNg`--flmD!<{bP~LN;`hIxShPS+J6VU zN^)x2GcT*$AGW~S+tI<3_sL|;j2cEq!2&v)SpA;f@>J~sKMn3 z513-AU?ve(eU9?Z6SV{_N1E^NaDP_xh8t;)9wsIz7+&i8ql;oKauAI;yuHC-k{DZ1 zG_nQfcWDGIpU4xKW4k_*wVL7+bm1ryiA;aVuGLavN-DAtlJ@}GXoYt81N(J$dp2;Z zFdn3qvdcnVvVvR!e=zQ+8e+u6$X21yc4v!udQ)`WZ{i+GlyXI*A@;&Qcz4WDUu?d!|vWKj+TFj9- z5yjl~p0w_=1~5u6s*ipkmwz_+Rv+#2ez;{R(R*D<#MPff9xrFbZx%tOp}C{*2A3;z z{~bnBm3n2A+xI!hY*NeJXRjz~G#?HmC=}?^=raCjmJiP^glZ1Qy?iW3YVpBkKN#zH zzfki)Kv1qIp}4(0e@p0FBDV3o2Fw1~vOtWaFIZPUzKO{0hk)+FXy_Xhxq{k@tEDJobDE#d(EO0y1J zH-u|=H6Jh71mbrJykh3JQYSE{TSx&#+-GLOIb7lGqz>;gFdH+73{p=ODN9}DxV@+g zSsEcIL9dkjAJ{lxJD0(V0u+}qh65G1DdYl7CjvD!moR7p6%aWv3NK7$ZfA68G9WfI zGBKC&p#v2HG%=I094UW|cLh|GYu7eXA}!rKbi>doNJ@A2Fbpun3=Bh;G)M@7q?EK$ zf*^|0APAyJNJ)rvcZ%N_=bZPv|G(Dvtyyd4d9Ho!eedhOuYIpI94z`q0*a0Z2dEkX zjuH?S1jzu(Iz~pqAOHjs6$F7q@Hse)VJLU#UpPL8DHQ1qL%@G!{)3^6go06MOa+WW z6Lk=9K-0$^5Ecc5C1ivpWk4W61O$@)i-A_VLX7$G1qC@SDzCAj5WP$*9s zAt66MKS8jEw;+E4=`7E~5BR}QE`TA_8;bOWIs!i>1G-=j=pSi<_#A+-3(Wg>zzE@l z@&hBG02*+IL7;GNbczq$5sC!Rs{=+F+JK%X6#kp6{hPoK{Mj5pSWx)ya(}Xa7J|Wl zC4(Ulgoh^>9sq+o15Pk^D4?gNEr{|*@dIGE<4+>k-5Y;_&IkK~VeViDG~rk4U_edL z005&E{?X?RLBc#y-h$pR_n#VtewIORvnt$C8R6jpg`>RjfA*&WLqZ|weGd@&bGUAB zgdaTUuQw+c+|lW$3`ZYNArm;v%Ll5V@`ro2Z zIKUJ7D=2^b6Gm$Y3GzgE0#0Zd&=8mt6#auAb?p1c z0^EP*g9rF`EnNiKxKM!mAD>%;#6b}Bm+=4D?Y~m~e?|&i$M4 z|HQ!_F!z8z47725Q0O7hL7+zg{@+wH=^^lf&=hDXfKI~ivvNz=y`O6`v0;qAS4J!pwKA*T3!g?gh1l|JX2|5 zK*#|JhPXk|cc2sMXYe;J`Zxaj4F3vH+(w#J}V~e30lA)Gy1Swf^<~>k5HF{h<*2$#DckHp;a=>h)=zBE6r$(tF&w zh1U2Ki=H@f6zAu{AQ$cHId?vJDcL(#(+p(lPgKSXj|bE9Zl4bMTX#GPpUqG&Kw*EV z_ADyx3KaF99dtNDnH&*x&L1Q*;}>u4q#7`i*jr&+aiy7!Uq(xIimucwmwa#aUa^$k z06c)QV^Wq3dQ_5Pe(MHif&~lv0_vTGHEA*#(kjcxSi|JQcgzG>@`=*xMB1}}MXTiO zcVE8rJ?ibr6y1NU)m1A|9Yan_5vYI3l0Wd^EiGm0Bh44y7@SJt$I6vyqC2Bq7=;7x za6fGlhBz2>9UA0i`eManU4IEN$D-RXZA#x})^1M3qmzC0d=%sSM*N4 zO0LhiIVdaEHNnKyYE)9;Ko)#2arC5DWEMxw60?{LFCib$Zh|Hs!$jP^)%1&l0 zI-IN0W^YyFBb6nAwWgHZYvPv%*Srau6fr>uW}D`T09fqA={iI2aZH)0L>6QHLqY~> z6^7L|sqOX3B7f=8Fx8?%6jp!G2EI@5bx#>a;a9VerqjTC;zx98wvW!$`|l=-#5?#? z)iw5S6yLU6yqy(z!#SF2He%9d_5cUO$xk@!aBb$bVa*I{4lw@){5q`X={A?UOT(u*F2u{?+}BKt(o2(jl&7?+*VBI;jrfm?}N$;`;nOAHLtjD zIHkX7fy`*L^h;}R=@DbkN7~U>;6b*at)4EKyNuS#+Hd<@^(KFG^DMu23Am1o;LCgM zRrE#Rr7DbgxlZoov&?^iwQ<)EYSD^Wd}p46rW3q74ITDxjH2a(RUn%dRO=hQMDH>0 zEei0p`hA_8TJ>q5>~XYW37HQ!fa~m?rKN4A_(;E&#VBjUXo-CCBFJysk-y#fW2)+hKxb^D1A2P7ODK9u2B?yGBxJChELY{ zcPvb^_z#N_r`BXuD**;U?@6NsO!+EUk3=&0FwT}z2F#Pw9U znnhep+B(R2dakQF38!D5U+=n&b;fky>~y)IC9bX0vFU%IH|4cbx)vtmQNyK<%fx1G zeL4+Vw7%$gU;$)s20%U!8&JIuQC!se&|hXcm5>e#mUoeDiP1ZtCZz~TYEJlarDNAy za73~bC=p|>)-^F`_e!%QEFz>sX03`W)`EosXLjdubpA(GjMK>xzfnahM^$Kh_g*!geM63#tsC4QuJn= zdtaryX5h=EIhH+;UhPF1s8&#cEWQ%4l*>=8DXJP?KK(|5^VX9gdiLoYhMQ*pI)s|^ zQ|Qh^Bg)0c52=QRsGc+^rrVWMM2Xqv2i$F|x}v`?Qa4QRUMeCGdN z5^BY^fT<`-aXJs*I;S{2%DWF(yCgmhV8*q$X7uA^T}FrvJh~URu{wljkJgB58Bq3eGn zohQkP1hJHeC?lJejaM|)usnA9aipN9h}S-N`Hd0RlTBrQiMroIV{0b#7N#v zIjWK%S1mYowo2x6X-FEWdojv~D7}o<%S^qg8&LahUo4l#%u1p&mWlj2tQvcBcm2Ni zofGmO>Cq2TODU24$x5d0q4zxOk@A0~M^f~rMuT?`q=F*CU47d(ug8Dos+!W~ zM56m>9XGqo`tjGa5YfS!(!SC1F5`~W8o$U%d&ik0TA%tLj4EWUS>}kXFrOkX_eWA8 zo_R9@c#rw8!-Tf{!ID8hH|ZH)t&OIoYDL$e)Cbd6gvC}2U01R&2CiF&d37kk);X7Q zLt3@OVW5A*QPcsMXTY%97sr1q$%5!4=YE%gKzz=bk!9_t`^8agdp9q-ukrFrXp#1K zI;SX%=nl#x=aIcABvpdWs*T`t2BaPPn5T%h=tHVVej2YqW2kwQSDs~WQv zaW@JWfffq(cV~30mG6HObniXt8(p?Ad_SmCB8E7Y8GpHGPW*&2?4?tz#hRDvkZN^^ zEWUkhkCG%sa4=$)q>cWm3guItTB~m|H!q8nyP<9zn~%T=0RoHmpV*} z<`U)A)4P|e+xoD%W9Di0nF2W@zKn0sbuUn(u?#c|BzC=6M~JJg%KEJ1Bazcp+nf*T z?lgbXr84+&UPD;Qo_B9Z?yxH9PG?IcBuMW`?#VT74O1BFsY{N`c;2TDTRutML)4cA z2Ev5!vj~tcb%}$Ih$qI*yUk-^C}dR z{b0H^l}j^PH!Sh$M|Qmy#><|5*_YzfvBYfeGb!+0v!#C*1)eVLNn=%Ki0q$?^!bSz z+jq@sUch+hlkjfwAcitxW#_A=#)C}D|8+V>sj=-OG|4*Q+fu#3jYuWB0&dHtD2 zRs%`efYQ3ht7P{Anxypw<?=6LATm1}tR){|Zd&gdI>mhV2z+{IU+sBb1vh_AEjevtA7zDKYg{pXC107@n)+FV zEE#9JC;=MODICfmxwN`qMr10_bdsIgo-ptSv zus4RVqJ*K}3NbUgxAtM`Qs7lKRUjwZr3Nf2oe92=snA|lNU+_bh$jVEvQFs{Sh%nmI>Y^N#DjFH8l%>m7 zr?PdJeE5B}1_8P)jBvW}V7-YU`}sMxc`H5}2i&ox;E(d{JIgUi>JC*K%!uHoM;d=Q zU&M0CR##%kWreL$YrIvoZOT~Eu0+4Jb$(@>-W|3wUIH1F@nWm<41MC?3W&*vCDdr{ zyVs16p?1Y5@2ba>Dw~qTvpk?nA0-lOItP1y^*dAb7gvY^jXz9eH3Yw>m6Ie979Ns* zK3y5N#qG8`1zzvLC4l<`J@9g-i9&xuNBq_pn+kTga364Y`VP_~uu!4+w=2#zh1*Sc z-xN;|YfSK`fl6-lf<;vl#ozJIGzR44YKxWx`$kkX3x` z*zH0rd*G$maemKeLHeF2FR@2YVyM5MDtTAoiH*+CMV_C5BJVdmO^2=6SJ!`e(m!Mv z5Jr4>^l0`pvqk!oLRv8`No$aTBmsvQY3@1e;+9GfKOb#PE&4A zrq!MZi_wNFpiQ9(86ERNNpqJ*i%)-sR`@g7S&-|&tbD6vi^+s>8;9G&&y5igWfX^&?sh@k zG-h>sk+34OK|+OvW(G@(rL2Bcc_o!G8-@N2>D&mBS#9TrOuJ+#4!zclrL*m`4$tx{S9}g1X?1vdT9>Z0Qq&X7Yc}Qxt2;g{vPX}kAf|AK$oYF4mwtVMn!v+9v^o(BKJM70+}yr$tI zpP^UGvaXgGb7M=&-#Gl`jx}T^D15K*bTZN#6M^;~x9Z!ShNpi{ccFQjK6tyIpO`c%i)Zlj5ELvE zSjAK;$9BFUP4J^X6Sxmt&bBI%@a>zn3Mx)*&-kos_L9k6edujHPF12Z^3SAmV?3vG zk_TF_!Dn_mc|Ff)3sAoEGlaFCTiR11*<1Tm&?l&7korcw$+hdwah{rxk-Wy%@X(N z5fbMZyV!qg0sU5fpVnVv*ksbOCcjh(YyYHDmF9nM={h%9h067^Rh>G(c?q?c+BS_-Qq+)D%~M`ir<1L5nKR4TR6PEnu7M6nGU%{DRokQF+b0(J3&y zs?_NiZNpCs*iv(0v(MGY`|?!r6^n#+;&UR_Yw=mO+5V#Z2eXoBoj1)Oi7MtvZrA-|EHR&E=m#xQjoIXiFZq|+ru=&WuZ}+J ze-vf6!};#ZyQetQd9d@OmJuVF`tvBW>$Z0nTmLJL>Re*R`wz)|hQc{saPHwj2F$fC zlE;~RmTr#Wbq|$E5$&heJ=wkYG$vKow(ox%NR@*&`>X1ZndnhHOs-1igdk0Tz338075hcbG|r2Kg1Xh z)6?b#0n2+cT$L}l4J43ygZK&p@Ui0R7UVqb*|R$h93OdI zlssx>Bc`jLDBS*rcvV-C%9EzaX)wV-V{8{M_P(FVkX3!2+Jew(xG8Sjs8Ztllx@=E zJ^OX#kWB%$26tiSq`?Z@d^gd$Ntu5eQk>_U45!79O}4b9cD`)zT&Ri{#u@4>Z5H4x zj~t@^VcXx9_9@p02(uqFd4^Y*fMxCZTzL#UEU9K zT>7bj3DfmGwRSo^-zT4?j^ln_uiCr5PTgB-gLLATR~8CpS-ZoMTge}QKdXNdNvQ0V zEdTlA4k^u7?%b%%-K=Nr-(iN)adTpor!<_$GlMuE+ zW<71#2g`p!@^@B8Y4feaT^SS6?>ZcEXQxV8u>x&h7kkeM-p5!* zwHw=5J0O*pP8Sx2{Y5!80MDAsV4kVS0M|a?$YuP z#=&!5zD4D1G#K?vEZZ*Ywouspq96(v>)fZEp+(>P#vVU+Ir(_AI;4L+v~#fPqQ{P5 zW4FcV%BEJ5!Lnm?V)fj1K!cO`&a@;zIYPP?=jPTdc5rWj-GPk>)lRBC<>qHWvSQwi z(m>Eg+?w#havt8(>D5mCp!~Q8!@2cN48MEd`x~G5Jg{rEkw2sTOWuKyDGHfn{3s&77DVwJE$YbbwAn<}J-A z&+QSn`nM9S1`(uHEBuvM@mNC3E`rZog@tnsf()E9O-Q$aZ?Pr_o7!j5D14pr<#pdl z=FXnU+OwofsL+!_jqQUE?x$**SKE6C)|56-x>45C%qkIjqdtEFLL~^!KF8Y#$8;6u zbD4QrRTk;PmEonV?~df}7%e%xdgU{lsj572%*-6T^nwOS!b(L>wc;kt7_^J81yC;8tAwkK?jQGPVWZhL2#>R3*%5{I^2Z%mg-Md;f#*2aMxth;v?jUV5 zP7QjpZE!aPvzactA@34%<=0OWrFruUv>xGfbt5kPv@xAM+GeFg2&Ha2}Znid*N| z!Yzf-`HeVRVe`05D7MPRWiXT5l^(I$n7$(%Yz|*_Nm>tlrhyz!zar~b)T2&~4R^C` zu1B%m+R#l=KgYPiQF$9Z{9|OjM<%>utajpY)7nuO-793*Kqq2B(x#>Fr<>nx5~Pr< zVtsg~$Tfd9;d@DGaU%GZS0$`2e7Gn+geYL~>xBM5BS%;IiQ8yUK%w$&`wSA-aCzsO z<-){SLa~(?MpLA$$$_yaaSCg16!ggijN?P6%6fiygxUmu{`_+GNl`=^-lDnaju$kM zXd#zWVRi*?q?;n?0prtii~4D!F$ia~nsVX`oI!tneT87NOTn=CtYv%#H5{y`8@M^c zTBoB)MGQ;ouz87bGo$A)mKo;UJ_|!l_sqdQN{p369gGKN-GR;9w1mSCxU+oWaZcgk zI3r4?c;q?>mW*~(8&b8=tR1O=&%bAuc)M_`3Qf&aSA;((E5618V^};mz!^KNxHUh| zd;cw<9+{-6o?(EuI^SiJN1w}KD|ku%h!(S0_v|8orOHrJHo;g>-}iq2%Tix8m%)kx zDYqFb1L+=@LZ$*15H&D13NK7$ZfA68G9WfLHJ2g30~G@|H#L_*D+4NjbOltDYu7f7 zfYKo$K7e%1&>)C3NSBhrI1Df_%n(C|fOJcv2ug=^DBaR2B}jLObcn)-an5IqB{g+%j$`9KnYoVu>AH~<0(@_|4CgsiN3a5Mt; z2PR~F2y=9TqmUB+;UMRK2!o)nFnI|2%1<4I1Rgje0I(nc7LfppN`OFs00<=hmm|ti z0+5Hez%2lEKHvcg33DQ3l|$LPJHoB3(N|^u`U`MCIRUV^xG2xBa6rZm<_L#EkbpV_ zZ4I-#stAQ309_Om4nw>DD+Pz7H5zR%!O!pN>dFVPbK*leTHWJ+ehf3Psu3Ly+!pq!nNZN5BA0MKwOO8=41z zAT546LJ&@<`#3j+Uta}3Zwu_{PQ6d?Zlv{%>k*NI*J2!P|ya&Q9w&ZU96QY{SN_($X>AYl;n z>I?jTmin)d|DVEtX8GSl{@({ubVeY4(Hy_{|A&Uy!4d9%9Iiy`jJ{d`b=1`wAphI- z5$yNUs>3Yc&UXLJs-PiPiy(uvLi~M=a3@8$8_Ys~3yy|b|5nRyT>s~mA>c@u7Rm|! z^H~9S!649oaaXGawY_>eoUSDKi-KKk&c914AfYIWpDQLHECN6r9U<<7S3|zCgaJ?R z)h=4V+MsNgy7IO94^s@_{}1*`?fj^}O(77#Z-4dTApccO2>b{Ar`Er^ggQH3 zk?3DKyt;zF>|d`64CV%d5`G#-LGK3Gln2zGmden(@~-sb%`G&B#u#>l2&361UVB=r z;lEHCaga5)D7x93q&1lzG(7G_&$V+p;AYZ)lIk~`sGN?*Q0Q2a-Q|7LeRj}d1!J^8 z(OC_OCWS7MeSNNdi^SX*$Cy3t(fId3(N@8=;?=AljZSMu;#+_nkaJARlt_<`lu2vc z!U{KJy0d`pF*G5KB6l=ScGgqTAN+w84v#vKt&gBRb6+x!O6i%I=}P_3ktDdEq1sk| zB2pAYK}+fJfGMpvwv(3XdFq3AP8e*m!pCy?ae`k*+b~}D_TY_dU-vdYq%Er?(R0_> z-7%-md#-qssRB!$x_mOSMBNkc*_3^xpEp`shY^Gmv@v>b=b8dJT*B%GXkX{j^rK)$ zyU})X54}r9B?~4c#v8sBE={&9j1IYf8@Ebss7SHQH#O+m=uk3yGnQ@mhfo&=vFG({ zx>Yah2PllI!PI5X>ZiWM+D(=)DF{sirWG8TW_-ZU)ch%OPG_cw@%=QglMlXnbo#+YnF0W z0GB*_6Qx{iT6b#p0J_62wlYSO>JAIT(yQcprTTX@whNBS#A_e*o?=pwg!>x7#<7^Z zd!#6z9CXnLY}kCBX-__Tq4JdHvhdah>n zY<5nIV+LWtukTN0PCBR2S<}G@-e(1!=w>Zvzkn*>IA}CF{eV$U8(Uz zlbgpFV(6h+dAmo-F6x4RcCwG$MqE}7kT8XowSYVe;@j2`K^=f8C?#=6`G$XQ4mr{F zI~Med7iDXB&_cXyLU#r&)=lP$L_Nh*-L_~h91%Rfb?VJ_vJ`nH-@>uO3E4ai@K?NB z5&PsdT}F8n;BE2cw7xGgMJ92jHr3PYRBOl4C)y9>H*_v~#XG!zwcpH-E|}l1xS+pm zzGi&yi$P?B{}_RSGqkC27TD2uTo{Fyq{Y^f6iG2@I}$VIB@RqIN1tDzQQ(fPLuSeGb!)t@q}e_H?$YY z7H?^xSN*r4!kI3ARpOk{8XFifu?k%GKT)qLtVR(}1(2Q#+TS>M!rT4{Be3j=^Jbax z4fdGZ^i`+ghS&{3kh>;k;>5=GUs7sH6W4JHo^czhbzMGv+n%0HhQBo#EJJeIH3Ak7 z5}YYQU?q@6M2k_R2a`Gkt&BJF9Ah$d>M0Eb+Yz13?mW4Fvq#Ji>RP4wK~veM`c<3T z=-Kg1GZ-H-d7pV@kg~KXm z?%&ubALz!It2`tNrZ^{lo{=sdCXMCG@Qy^}(P%91tyo3zF~KJcH-F@h98y2ssEVXH zIsZcDSb9)@F*!qKbf|A-gy@gW7Z+wQJ)HaSx>3PROvo&dB{l@MisbI|6ZdTe0;+=zdY(h;k z7XzVg&9D^F#blKcfxWGx!8cKg2G}J#mGv{1&8D7zDvy*))pq4G%mr(q4b(oPR^?Ii zFScZ-*XkG9LkVcIuwvYrv8WOl8&Ae@Q}f=f^~v0~>1hrTqpUhAD@ykiCCH>LsGslk zWPGaq!jXNxmSHCcbFfl7QQdY9dZ;?=BKVqW@8E2Kj`c+3X0UrYkLiL6TTRrDdWiHm zCTw|s^F2$0h|aY>yD~bz)5n#igb8_;9%BvBcU3I=<8uJ-qlW?91=TD%44ZG=^=N}q zU6b+KR~>K|xOgZ+)Z>J4G%8+#MP|)#ZmA)NylR#2e1FY?Eiw-;`u?zZrYi$QV=4-fmr%lJ=KD$Xf z(u3F~SIC(jLftUB)Zo(@yXn|^IVPP;MKqLR2n)j;Lzw zrg6qvM_Nr`Z6^drY}+sW6dnrh$8+TDy?ra_pWc}3B=4ZRz%CIZob4_hLEV`#^`4$x zYtBL6I%+@)Pd?zOiszG-vcxUjXg)`=jaatWa^>@ImskT1rkA+;1tv1qjc$uTH1Rcw z+t(X~qRrf;^E$ti+ z-bS~|+{ZAHe*2urS)coM^UiwhyjS-8g4|2I&x=a}<@KTadiv!op~QhBVuo{P8oIZ* z?o6GZioO)&;tA$C`l{YhB&k(W6o(TZ?Vw+Ttyo`yv_8IRKt(87Y_*Brr+ScmcCKRTvc!7qm9ek#Po3Zw!DacT?VV8exkpdo{wFq ze#=*}q}a@C#=fIDv(uB#w5Ph()DD9D$AV1U@-p#1Hb+Pz0TAYnZ2}Gr@XcO(-VY1$fVA%dbYe@Ka5QumRV0_1A zv5=;gF7w0)Upu>=E0*@P2ft}!4@X)A)C=D<^8;XSQBY#PNhUZiL2oB6Sf<9Hz0x+Q zf15UoJ;jMn>srldK>^a=7~fyCLd%vSZvKemM{_L&Em2&5gd}6ml0w+$^;a?NBt`Mj z5q-tGft`??V_0~Pmqg6+viPFAgWq~qb7G-7AK27Oo4tE~)B@L6Q! z$$n~?{xL$|ac;+5nAyQggHnoZk#~=fbHPt{meA*JHm@3k$c=UO?@1?8>-j^Q0=UIe ztgB81)(94Via`USE%&=;vILOxY%a;!bpU50U=b(jKsf(fV`SqFBF@4OUtodaldM&8 z$jV^b#QM`rTlP!4akeFv$6*`%^H`^EX%UhrqBHVcb>b3Zylgj%nq;Et&Ag)p4xvZ= zaK!aHPMjfV+Et6Bl_GA19~uDP28~c5M37HEmi?q)+6G6s!g#i}y*CWD$aB6ebWn&wjCVOqv-QH@r zZW3{1O6Y)TsOjk6oK`=o7;DxV%PbV>-6Ri|6uMn{kx6TQ+!<0xCeyf6N(RyZg$3dJ z>N<{pYd<8F!iDQc(X_qs(Wv>Hqlm3yXBMAO3uaTby2o@(bDpq&WaHWT)DAQjqVC7Z z^g#{{sJ6Y{-HjN7QA%+PFlY?Id0Q&8F@oQm+D1;jAUb)A^}208KScAb%NH)U4MNkE z5uQ5rImL&nsoLIUNx;wav9a~<9~2HezP$qoq?x)N;YJ=m!e#tZ)wj5B4zIi%S* zUNYrf`nxSeALF#6?aexJXeMHRy#<*IB@hXdCv zaeSDvDznhMkjQQO&h}xWpk-*Z3r_U&`xUXB=Cxn5tv4`itL(>E{V49dTQQ;co=w`y zqGoknxyCeriTj1f)jfN*sqy*qc+H8D_L=z`Y~nK`^Z6kQO^4tX`uJz{6rDML15$fE z+GUgid)>C7ayVwM1@OdTI9@KA7E>pFV`X^!i82C=IXZ)a`Qs()(Ld{NvTN004=8$l z=!LK=*V9@G<<}byp?@}i_ubeBS;Q(|VjT7hrt!vb&#KWh;Wc!>q(U0|jMg>8h0Ddl zZ0ktHI=6HJ;Rp3ub)=2|;ZO%cbE#xZXk-xusw%I$o ziO5hvxf)JXds3%r_BkbqRnarJi13}KpoFXJ!@^eFS{a{XGuat?*UUvrdy~tkEN~Ao zGD5WzB$rL|gt$wZH)B2RIE*}&m~bcZ_%Uu%QT6UC_|LIKe&Z6U+5k6yO|$Sv&>eQN zd?-HZ1nQ!`rCK_Ry`QLjT^IR9xY$UNNLj1(R#L!^!2qHgxYEYHow2>adPB?$%x~*f zzbItKoX@Ou2~Q2F-yd%9(%O46hPHa+39FcQ8duM3l^ zJdYdZH}{rSN7D5; zxoB63`HgH>*7D7N?!oK=)q~LJ)Eqy!-O;gzN9BIX({KCM!Ih zxfX|r&(XFG=7F!-cM7HPp9iH*1B`=;YGn{g0FJ*tcKS(wpy9FgA64JA6;bkW995Qtx9g~ydRv1vW5#-I&styswT^FyFQJ`QO_NWs_K7MUk zJ-ylJ3(@O;TG3pp)10ez=e_NTvDS}$j9oWcO`DIB22_?$v5&$BB5r{D@$Np4vdyP6 z(b4kQ+MLJfRrp9v-q!MhmhSY^R@1$(EH*EX8qVTYLAUeGTHG|Z*V|MI@|N$$Lf_)+ z(JVJbCpq6peKD+3smnYixf#0+rUZzZ-cOK&Q%BfcS#c*hO zn?`GYR1UDHUE4qCeK=2aKqC_{&eRXS#i+Knjo-e#N3nWQPr-!8(O6+yCm?Knv}Je} z7)IdWL86BhJc;G6Cq6;&akx%22%G&2k7R!VTgbdQ#G1DQJ;LS}|KkCO6cRxCtk%@B zo9epsBT@&hC$!JbI$kJEPq%z(ddJ4VI`>(Bk8LhO`k_LRs+n*S0Czx$zX$GH-HGVy zBx9^$vsA7NlWi(sa)q7m6^o6y0?XzpZ(t1N0ZxtAm8naF_9@B-8>k_j6q$pZq*!ql zk@yj#uXK>4iAuL42(|_Tp=K|ZoZd~U5+j^n8ac&^I`F;iFbt4KzueueTgipL%C1OD zvD@2hf34GrR8;jaxj#aMfho0PPzK`4t&x+xj~*M)DlPZpFV}w(-)wgBSO{}Pj9GY ziwy1Yp&vrGD(R!WF3>ZrNjko^#bCU&dc zpV?Ijd#-BD{ax4MW#^AbrBlr+r@Ha)wKl9kq=$R-b@$pEMao)MzCT(-t5`28n@)<} z@Yk^u-mbv6p~ickK9l3@FnT%f7XySL8eM~{DWgnBdMrG6FYCf1!C%Rg7J2dnsmHhv&WEP+qk!h+t>sz8+>s z=C>f$zGTU6tf0c2R%;CN!74kFI^P`&<=Bd8x>s@g$c{Y8^^+j}ps)Tq-#^Pu%()nB zNbIves6kMizw!M<`?;%_IAkwaFLx#!w!~yIT-6>nO5e5?up&X_rA`(Z zD4|wxDXds=Y(|5!^!=x;yO~}!PnT&Fcy?&-AOMvnU9lZs%3KqZ!jfKiD6yeX8KG)T?v3*PFc#rzo;QdNqj z7@Xei9ku3=8~Dq2gv#Fz{E#n!jf}flBX(}2HZeXpjiWs-4J>?2qb+@uXd9?aUGn|h zf`Nv}=i`NBL9#<@j&gp<0o|!ivD>6t$K6^@tMqrtT)1&vlkRuudWR(^f6GkjveI)j z8KgH})$GoSm9aCyhYtxniC-s(j#X;)9FVHbkB{IR-P`9OH89GY=4&-K`EUg14drTZ zo7#47FE+4&63|b^&-7P`&8M{@WNygp#YK?oU$Nnf%mMR^p_ z3_WFa7*DnnCsGazz1bWDv`nD#+e4W5yhul8ZO|e0-PNM?X06O0DrTk4GFW95GpI`N zHKxEr(w*u3?b;50LD480`TR}8F4Q(0x*_N2wwuNkWif2K41Rdq`Tx1-b`t>hm+ zJl9>#r;^tE`l1=hR962k>$5YHnMKmf8l&Qx3Tc?KhA~H#LnTa zZzid9`wuAiP$)A;zaNJ~>7TuX7#<*FglqQJuyWLsGIImmx^Ql*)`-2xRqz}2Af#a{ zB=Na)z8BaGe>WpKO;YQ{jJo^e$h`Qglm|EQR2E%eVnhT>XnJ4lk8te^rjX-I`VIIb z)4eV@PH&r9Fv!?aZ`V0&L_bMQ6xP&C=?M=Cu{R= z=Za)f%%wO{lTmqCmw{B$vwN$wb#Lf?YiZ|%P9X0VL#ja&D`$PBUW?BhiR`W$8)rGo zYKF}J1pwFFAVrtKiUJdtp~eCimn!4}3AfRJ2Psmdqf`Ou(iNmhN2E)UFX}n>-gEzd zt?ye|D|z=b&&;0L^UO|G*qHSU`4sHnwqR8_49O?NFDM02(l#^~5(Eeeitq~x3KOug z89|Ux@ZUHAn+ezr0fEDRr2d1Uw2i6&~pVSq>OP=JsKKuAnVNL)%#5Fjil zDET)L?j{9L2BIK#0BwH2BRC9Ed`ARrWA2nRvHNUwi{;C$$aM7l}|2zYpS@B>|c5d3gA2U#v&fCmKW z2rvL6z-}n89pG2V03DzU_^;CV3D^Keju6BjgCX1==>c>D1JD2z0s_MjXcu>w9oP+k z-W_15p#{)&1;hT3wf+!z0e>A1K!{)HU*Z1p{uKxU`|S(_f#5E#K$sT<<^ZsVK*0cA zRV{v`Cz2NcgxURnA_AcZINBeG0z#lbTQuQ!>Og?1f<6Frz+cUEhQU2xK7X(5Auv1pUuD?2y9yY? zAWz-F8p?lh&=A4DF$XXbASx&+CMGHb06ztQJwc8Fzcw&`@^S_LHvWduHTe0s!d(IO z=rX{55PLBCgTMy?M1cWFH+Qg~&wm>Jxgrn}0@y)7NPsQa0RkiVcXTuiw*Rw6_umcT z39t}E%TEX(`0MqrCrh-#?BFn{*T3n%TP&btqN%EB#`9On|1v5n!aV^#e8Q3dK1ne_ zfRK=o7(iTqTomB<&p3KO$X|Kz4{!SV7mj*ch8W1kPKV#{@(aHq_ zIR7p5CxW7aAoQEi|5@(8UH*S6{}ttb75aZ?r0NcZ{n9D-1Vc!KTpAV`qo zAJzQ9jepG>6aoY5!4Z&OHw@ZKQ1HKO=ph3+qi+!eTAII2VDt?Ab6*u02yXXl*n~yJ z06;f4pcetU*XWffz()u@m3Cmy-(m&`@WbFpv0kjv~<4?QL zw%;&++FQT{=!!tX{{;y@00|W6frRmLhk9aD12d8rw6;F#?CHt13&q!ipfo;K& zWIBBnD&8fsQT4U(x&yKCSaJv80w_KuWlp6>#wg@=>|j1OXJ%bS4w^q9jU{)pNOw2V zFdn_ed=7~{Q*4W-{o%E05t}ji`SbhizTPyE!vf9jYOz;g6ts7}A2H_+CHB)&CS*T< zdV|1VR}?)}DocrHp)GO=V_(Fw5nsHVEa+=Z=ofKsaL`c-yrcWgyQIxlK%Ff{?IfiVReu|I15%MKL`nlk1SZ>HO8h6aWPs8-<<*mJYMivis0| zUjCDpy^spCdr7e4&8GKlgI(bVaMpcB%89^uv-ZrLp6!G;8ltBk$xOIa#E`X`Gw@Dp zuDIN|Nq>X9Tx`A84mIh=@{+S-EO90xB>6^UGe%k)80k9r=2D>w0h6S}6wdp9WsFtx zm0}-W$OJXXq(}q^K3f<&xTUPW8%J8ZdPl$O$8D>4TekxNVLU6%ypG3?HYGY7kB|@I zA&oonSDT9F6M+Zf(~EhEKaKsZDN3I`$+M5Sj6K;nZupjGrcf907#f+}IpM!*{qh52 z`{TY?hV-{%yWQ;^DI1>Z4Hu_>*asB0gKAR!EFneEipDt67NyU9l##|3eL$_Y<0d?} zt`GK&$3F;#EY74KtOdGSZ8qq9bG4QxeVdAe$bRt1c z1RQG!5}tsG360AYxH0P-bb}U$*H${=vU8uPpoOUyOrf8{U5O~yo?+rS`Dhg7S zxJ2oReEK3_rYrW|O99?lyZ>xw!_xFAy>gItGfss0gQb$AuqLts^SG%G1!j6G2U^#{ ztJcEJcV0D@!PQfI2!yCTCur{uY;$zBxzqZ#!immLFHCjrF&bp5Bp(*(A04JyA{mb-+lk$!}fglLxQ4ot){Yut%`J^A`_gXYA5 z?58zy(o{O5^TYS_es_7!`{hXP49S zg%ij`byJ1Xu2(D4b3LJ9YW&8yq; z_XKuP@8U)+@|!PbnVPiNbo1qRGSq{^lE@z}>T$$AdO5V`^M-k6Z%Pi;#A^cX=NRs3WbrMCb4n$HSjn?abB*W6xKd-?Uo}}JZwX?ONUDX=i9h8Cru}+LGvexjv#t#F-p>m8X=6ZQ=_e@f zXFa&e^F``^ug;hsGq{Px@t$H?%roQco?W~dncbXx0&fknnqhsz9=(2aXh|jkO^I83 zgJ@MI>KD8+@Bk(Tc!$ZH)P3|j@wMRa<{@Tj3iJHJqDJnydl5}OK*=^-}MatT|E6+vzypi+dJu^ zy2q)1o1J}Ys97dSXF@NsSgHo~L@!rqI^}_xRX~yx?VeEfnACwmh-KyHEL(3iB{>$h zAYbt3A`j-x+SFmOn4uyAy-n7yZlZ%PNgqA?iJH)5D75^` z$48C1T(zKSW>K@X6QV3X+@PT+?$LXOGs7ot7aD zO9u)`?BqVzno(j?a0Z(PhxVK~>zFo}zui-veYmOUSC_iwT({7TyJWD(IX^XOB|OW2 zMkqn`PrlzRdqAtd-a`2*B;PBC!Z#^y9(9lW8_%=&!aSy) zWR(U`?8n!0Sc%$wJ2L{WP8q3okh7s!SL)!*usJ=Y`Yn{s{sxPhm>*&BF3~=2Uan<> zP`(0n{M+bc9^Z``&b4NfsqafsYNeHbQ1Kv5dwSEm-ivN|)_XFSYAik+N4)JY{RKCU3_kfS&$FiWxo@-+i-DAUxrXz*^0DiiOB}B>4#E;M*8MGrRgiJ^XJCCrw}~92lbk%#`)EU=uh+M;NY?Jg$;&7+Ca}A~ z#HIgh+A{6ak`E3_gR}`p3EyVlwq)Lt^uFD?ZWN)k=r7QNeAbfCn;v$%1kT94mnIqA z=QcW}@jM^yRBgPcc}n5wl;6UCkI#!Ue9mJlR5ShhJ7Kp*7F1d5oBm@gk-)P?Jw6!x zxb3i=rX6Id~XNlu?X&$ z@D_~8+fGp*`E)J=_lApxU%?M0;(9eb^c_COxMp(;Sy2!N@lQuZcwvs9EZg) zFKzRJg-L|4~pRql$?liI~} z)HgTXNxKo4IP6X#*#wroWH5I%{jQnRNymn4e_N&$_wb??qdn$u3}v@uB0DhANt%bz zhq6^L9G`1!ULnz;yGE0HJJ3wC3v9@_ z-`UlKugpz!Ci?eGm#D6 z93PL{)z&1UB4>3d9)o0s=8FBzfbYY9rIFskw6hMTak{-4@|*3sa-L)m z@vDHwhxyOEI9zrS4>OFn0wrCUtvfJF^xNi7);NlFvefFoE#FR;CC2P69e(R%Sx*i- z5;2o~lo+_Er$u$?}ha>wq4k4hrtU{UvTkH9(FpiG`^@}9^uNjfpw#%iI0RqZP@00 z%IQfJi^YG=oD$6&1o>r@RVtlU0 z(yBQU7RFQ2HeucY7N?LCtl&&J#e>+<(`RbcOFKB@O1>lKG}qvZkA~V*o$O27Oi^xp zRT#rlm#VLNv23o+Xe*S|u+pmD@6+TF+;3oIKAuTS%uDcsRkieEs=D&rp$VOToa-U3 z*LYgOiuCOe#e0eP zktJpnar}DA8q#uk%tnO~0RB5~5LCNZr^c3RRl$ie<7QDm?_4K}*1wGka0}2z%6iFE zhjFh4+A3jnJhLKdNA}VyxYg!EIU3JM<*ZC;8RUlE7Ma-DLMo@MG z2lJTclcxS;h#)P?;AF+P%ue8U#Ma_ouqXVH89^9PSBG2PGne$R6QkUK;EqGSgJ zCM!c_bv%`1MQm z`b6752Q_J_0U~8EITQZ*zbrv;JQablK*&cG69FFldR5zr5syq?inkkEqLIV~V&h($=lWeGFx)6;!V9Y=DQrPxtQ@~DW{9_Xy$ z;APlFOZfC!9!9$~=Lm$;-s$^Jn%g=POIc(`hoXIMjv0o3W1f2Y^PUIXmEw3SgE>Q` zM=wWQRSr%w+T)~0BDr$-iY4ovLy}@tPV`9coZf!yv9zL6Aa!;&k^5aL^Z;5uSh-jp{gd?>RR~h?cqJgk~__h#l~ntFKH40z%$M+o@@0k$&1)qr0gsv-m%F$Xp9T#-vR3I|GL0)~9TyxWG*Nk`d*FZRC}s>*ZCcE_%}(@; zC1gz{Np?8Qw^7;#o1~Wzb;)*qZ@${dVh=Qbjbr&O2#guUqLyY%=M=M4ffM2tb$~>9 zPD#1j=_gRRKlZY|``XBkC8_+SEzia~-7w2pXtn1T4l$3zRWY;IN~E32noPR5wC}?0 zvp5~(JuK~uO)`eMEVlih?L-Ty{@7x&B2f@QRpR%r=w_iNsu>!EZ=nbYyf4;`niemA zh6Lw6-?hDensv(}%iC)-af%fiEL6GRJSCcDXP~fa!`T}u?-MU`+sjn8^B`hP_9CHr z!%O~a?@hDu@&(@SzZAOFrTalXsRs|e6m7-Jpv>%^aV-b8<;%i9eoMjQv&+X`Xm?-R zeuB|8jQN!%-T#ckeSgrGl38{^P7I``W|C7`Vr^Dd1|*f4(^Pww#4VxWS!&AYBEV zD0{m;lW@kq!;&!)XcErh(&1o{~ zPtV^UZ1XCJ!Ui>#RHIS^vgNKmL}e@!SppIK^*Y=euZ>#j0OOMJH^Yr`GDb`jbYECD zU6ue$l@7OFxRoEG>^|#wB74?29eI@(bF%JsS(kQgq6mh)zB5oPr`zy?5S|;7 z-==;1V=M7fq0Ohu@$S&4m3|9FC})Z`7bD5{tww}_*=eVdA}KvAI`wJLdescc+eY&s z-}2G~+yxpn8wZIcggR4yg`Q|hV~3A>505kN2uidnd6Ps2N23;WUM-SYGDLCiGs(M| zZH&|BYRq{OE}DQpJrvjwa7xdl5Ls?{;cbYun^m-5hatdbm2O-1yl3pYEghrI>2l-+ z$=;olQfcwD_^gek*B=*FwPr4*#G;;tIsZ&M{;FN}C7J(leSX(}Q3S_^T!-P3P1yIwi16k0&aKFELchDMOC-N+^?F5hA8^7&SJyhNX>u^DSm z#n%`VWX&5}d1mT|ZdMRxICJxl{{g9#U3gFHB`_XLM*FGdD0f zlK}!Le_LyB8^;y>&R?+~TLjj;UqBGRvLpGCxUS^dNerW)<+aR~5*3n4oc#Kpb7v(j zMKYmG$xVwO+1b(T%KK<*LZ`sMBLHtbZlg5UmBnAQnsUC z%x(}MO|r4^kRrv>1P5u1X+mhMcDP5}6U5-_A!CGWPU8+ay~W+*o-_o5l*SEWZfs1Y zL6c3;AfbPT5E1I-92!jJG{z-}WFSmRxEI|d2NguK#<`ptVBmsIgA5=7giKH|6xU>o ze@_^fsS+kdS67T~GR7AOUo2Edd+|^m4*`^SP6I03lN*%O_yCX~nvW0~paBz&fxsXU z1&xu#0Vbdpv~)?Z0-6oF1>=FFCQt?~L<;i`3``n4gf=<@I~WWPIIZOkvf~ER8pBF3 zv=J*b7Hpd$`oj;R$!r0t(R0q+#3Frwf1{@bEr9S860VSvv{*m|V;Bo;VqwJt6ixHP zK{WjVa2I1)$-pDo0sny0Xu3vI2&aLmOM=l&btJlh6OAb>Tn+m3y^a7CYQmSJz#Lvv z=ERz%h~5m0_jEtVZA99PHw$o1!^VbA=fvCT{|>mLhRY?13BIQg!oX1mRx~t&fA^df zaR9&3cJSqwjXG@9{^Z4^Q4gEfuz+q38nri?ziGbu3eN}X#V==Vqn;0s+ktv8Ii0ts z^BIP-<3Js>v&s4NsGTwBq~+W8-DtQw`PjT>l)_JTU|jfh*nutH3^3nxcsgskHy`jV zn0$&^5hOB2_uig3-+liBB4Nh?e+L^PCu&a5$KyA*&^32m!&27<+KwmF!?WR0+qgR9 z`EWjOr>9Lf!`C0@`-k)4yoIN-VW9Q^km$BY%j6DCI#7|iXgi%89k%o4HRgM`*Qgim z$9Z!p;7YiyyAHQCw-2}79K#K}-U)_kFOVR+I^qSf+4UjucXc${i~fjEf2S|nL5^k{ zTev0HclP#e;oQcy*s$we*#b!;E#dr$*m}K$ZN39+%jVdYKMb~g-$r_M8~eH1L*n=T zqK+gUwz5&aZX+LW-$p)dY@-dk-jR*4*b2)yeDvX`#jUs{ zmL<&79bld}$2|X0);ZWDCc6!`DO|@kx!cD!`HivNuyYh^c{6xjqC@FQ z+2KzREQ4`7rAes!#^$gS8t3g!?CSODHf5cso7o1;T;Ju<;<_@Tv?wFwK?$59`@uz` zfz7br%<_c2l8rk+FDHzJ9h+o0Lx>vBa>;Cw9w`*g91`Buf7N54R0%z_RU^) zeF<1v&Sf!UywIdy?`h?W!#u%hwEGaR)%lA{hljLUABc347RcecmTV&RkUr8Mb_&MJ z8~-f-r%PBe26}Kv1ztuQgChdH(ZlM9g`hjnvhZpHzjIN1s?aL~;w2ywWGsBPT=Yj& zpv5wpLQXsUf059#9_UHY7<#eiyJFIu!Aabuw8NidIcG-y(9PhIM;Q~}4$e(GdG)RD z`P;L@ax_(TxQKxI*K)Zi)A}d#&i_Mvfy>GE1B;|IlSuA zj&MCgzbkv<*SBU%GsMIhg3xiQB;#q|=`$IFOvf+D zkdv~bKLYRV~ml&4`>#Ca*NNX7+scm2O<^)E*(u0)285+=%JZuRh zjNs8;>-Fh2Wu2#+*@?@@yMiXE)H3Nzhw7Cv(H#S<^NF$kI2qd&*1MRe_pqB0a2Tn zGMwhVBGmPXv;K=m#D&!Cug%z3x ze>L);7`OqOwjk%faAls2f&|$6<}CXQa0SLX>=Y;o4_!h2%wtg&^YPT2$#QweGj7}w z@Jw97o&7xz(1G}oj0!f-nQKJwRQ58D=WlkNYq8qjV)H5Q;vurYwIxZCn~(@D#bz4H zaw*7VAGcq~*SHe%2n3;knL>bH6A!@Ee+^kk=tJr&q*CT@!%bSlLvBZ9E|t zMN!@fl)*(M0}tvY9_FiHQD6rd1ZbWf>_Db67Gtu{^%=nAxWLnQ=;tG|og6?DAQEKp zSyxaTH@S`rTCOU@6_!in3|))Wt)aWFT6&$Y{^knXbmhvfHifQdlpUp%Sv;d(=Em+!xML7gpdx?nD{^Zdc@FUhS!e9tT z&r_cf9Ob%}i~pkb@JHMIWsDp(e=9y-cJPcqMtNkUGrF1DB%vj7kd7otgh@C=F7ip_ z`qfE1Ga9gpVWDayf1wGJ(u=u$ zlLaiZ4TPre1aNCik@vB+`fOGcKh2Z)l2P!JMkAmihrqE60C+lFUosicrAiHwko z@KD-?3cnC!1v%&Bhimj6`<7IC%)nu&dp zl%3>GBkpzqGXG}WWMA9Gq@~W!e99c>XyTZwoCJ-F2`(Ih?@Y=ZNJB+Q{4roDbM?iZ zAFLm?v!m(gY(AOR57^I!CwTJpgO|^ree=cMXgWL`p3d&=PR8%>d^|j!HNJM-mGAfW z61?l4lY`GZ-welne=|_`XGi>2j}6>FJs6%nYDdQ(@TbO~d>^0Pv(^mM92Je;>DFL`8opuXz{!4ELUhvAf8zW<``t6lX#Jyeg>WA#*hqrO$o)Zf%|b)XK_ z3-waHQbYAt9jT+qcyfyJb-jZPh2DQe}1fv)d%(Am$MJ;sT!$& ztFby!r)r`mr|r&U_FkQ-GydW*Zr{&qZAy96Svws~-l-qekLQzlTid2;re6;Lfmv<6(22`!A_y z2k-lKh}rk;e_;Hg9q#1|Llo1qgH6}%P?09O0x9tvUBU?X#^*DEg7C}%2f$FjF2Yf6 z0>`tLj~^dAxPs%LJw6`~r)%&)NL4(lU+Qp$$AVY(d{VD*$YOsJ2m0gJYESKpSFZNS z;RzgPHavX?%-)`l$L)NTlkyD$*^;}CeyUyxzkNSBf1gTy!GZsE>B6U@&Vx_SPu}9C zqvKQga}(Dl47E#j^Gh4j-`m-I1bM~r-@P5n@QXj{4A*0#J>m$NVAh_Ds=l-Kr?zVw zeXRG3*!eJRtAtBYq<&Suwo_?b@E~t);z9SH?|%2_=@$>a{r0iZtNjMa@Sf$r?a-cI z1ND{He_&O+7)7oZMW$SjB8F9UpVV_7zes0d`$dJ^O%&RFzW@E-_eG(wQXddbRfqp> z>UPvAdaXM1;rXI6;U>!LA3QmD^tC7xS1N;@T~&r_wcAl9hs~9_5qIxDdGcuYu_%*P zd)kFECf%AcX00;A@!1FPbvqw+d~-Zxd2&uQe-T>qYhHLGVt)1f)&A2bqH9^LE7t5? zRB=0aJ5l8~SGHrbj%QXW9B#xThr5pse|Q9i57yBL7KWX|daJm*%t5iNRd)p1XNLWG zQKK6X$&(*;51xJljSdgjsT4bhE&Laep{&=a_>DA5y;Yu<70RVXZ>Phff46hVFkQW7 zf0dd+R%Wlu1ha_3jTq?uvwu8!`9e_0Yd{DWcEwIEMYA;uIo%wEV`TctnjDUVy$HaK zSnJg{5B6UCAONh1WbA}G0OTLQ>g@q=HwU2o@q9S`4Hy;?xDl5ei>32ln`*ndp zGApvEG!Z-guy(_Ol59obZ9alH90Uem+_$k6a+RnH8GcgrUEH{b_YNbkL)5J-SX3Xo7j5tLq~_aY#obScuSB2uL*C{3k<(ov**vD|yV|9x-f zy~#}SJ8Q4K&)(;(^_%eX7@3Nzz%Y(*4GbD54hDi00IK??)-nJPND2r7NmB9in;~#W z_+OZc-vaK5MPSf>3jd3s>IsM92$&iaN8ssW&;T7TBmgW00Lv(VWfedmfFuYc|1ToO zQvsj`^+vz|`apmV1`Wqj@vCCo@tz15R~%uRe?0<(oP+^jd3jl}-{}Aq6x0UVX1!gr-tx^I}wf@FY)JQ-Ov~xwEy3QGXf2B{xt;5%U!}8jqvb-YpeaG z5Fo1mU@mYR00NSik&%`Kz&!wPUnf_IU**m4?(pA#LGUk_FhQWdJH{R0Oc(+lh;W7z zeyIGhP;WQ@=jjCx^#9+1f3B#&U;qr^gabIjT@Yxh|5PWyaOXd3!sUA+d;zu~g7&}w z(67(GpKcT61;e0``2Wy<->-z$Ep2rzW6?ho|2uZ;7RDFgFD?xbmzS0!$W>AvAS)*W z2>fS%6(cC(Zx^8dq-vv`F#!2L%@R)OUk!Wzy?>#<-Vg@-vz7sdAX+#;=sy~_13^Ge zgfH;_x6*&7{NDurtIGdNMi# z!vA z+z5k3{Cc$zvOu8!iV^JPztGGOnZuI_H_~=vshsf9O!@hQ1Irrz%wDNI8>OG%tZHpK5{n+brfnSATuDHCF zO^^D__$|?xi{8PO)K)Ora_S^Zwp(g{rE>Z4X*+hsMt%c;0z5fn;7Q}a#i``AZxF{= z^ITiR^;_F9#9#8X&G0hQHh+6c9D|5Iy44!X{saHnHa@d|cJ@uq>z;I}-9p`$RWcRf zOzg~lIy`v;NxkeWi8(qASR#R2kVDmHsR_L7#i($BITBRO1elZaTKp?^5DHL#!g+T< z1rd26$y4i*^3~0|mCCJ3iG!%Ar~7`XYilF9$a&_dmou4RHcqF-KK{3v3~l#`mjJA) zoi}VLGDg{~5v&?jd^_L$DuUb((ulH0dl=3)7@DA^0tvY3(*#B|7WhEv-cbF3A(@|7 zyFo-v7a65)&Wfx3;^?8p`MzL(^Na_X^O8We&r=a!bnQCM=2hj?rR>J-7mtZ$`0g>K zwVISYT1I`>TukZY1QtN`X;Whwnge?6=17Dn*DS!Zldiyf7fbfkVu!U3_0Pg4Wyy1H z*g%w@rP}H0XBgO)*F3?>Je^;)QlO6MtiFW zcdMl8rXt1a4gLVnju8CSlC4Z_5w1_9;8D}~xQbM_9=dE?&BKitQ!ko>XQIUN1tZi8 zsWRHA2exzt;%;QX2bSb949U9+!=DDKp0U@^ea+!<-Y2@0x+$;!hD{6`0o%XaQ3f^V zyf&vLptreb-#MjpIkFFbD)}?Ghe?rbOZY%eY=U0_^89PD!55khw5`PyW5cbe_%9@F zH$tRYq`2$c)Wpg;I+nS*k?XsjsKM^_q21l(R{5V9{1vB(CXez5Y)A@~790)zf}|^} zEkvSTw+~+}*W~i+(ByTs9*3!pde|uM0*vakZ6=v=iqVXATk{nY{Ocq7>fSSo#3||-{K`L1rM8uR#w)U<} zLKoQjUz(gmx%)GJ@odwW>#(Y=T2m~Yj|gdb_HuBvj_V!=Ixs`5Diktbi{6cFu#mauMLsa9I^6pq&_fT=vGqgIrfD%vQEwxB(GnWoK! z3V$4bizo1Z+Yw2qa?YPX(jH(IcP-M6Vi-m4JoC;=pHQf2JjxC}9=WdqIg+lX3K4!iAIg-ih>> zrVxYGzTJI!?`O-z46SXp?@fA+>MknDqSVWxgZV;^mQ``QG1Gimli9|OC4=~D4zfRAq-WfrPbp^7b3QM4cdU>SlkQM| ziY&QA{px`+O!+cTZ)={tyT`|jXx}s9!={ED%4s`}m{qzuPI*TEr@V$^5P$jJdAaiM zOk#yTL+S$TW*44dmV30d?&`Kzc;k7mcWadk5`e){zzKT_BV%xg#$zOw3K$wvQ1nL)8 zgIv4QQ`4C_DKajivwN-4UM_dHnD6MS6c=R^q9lx=r-_woG07}d-D#4#LaZTw-0?zn zn*~0t(A{lBBwG|JTW-`=vw1z|U#UJta`_ zZ9j}?>&DT^R6|=M=x}V+tM6RF_N=1tB4=ZLqV|IRmt%ulB$ZXqlbK$=NSoC(?(Sdv zS`b>(5@|gJwXBcy2+*B+A$z!gra@88a|*h17WRcpv2#d(FR=W4aF{~7#HQ~t^DaYI z99ur^K*#0qq;4nO_r~gNWqUmooE0s>r1JAc{SgiGO!RyLaYsHhc6$SUff#wiUAdqA zHe6VZ>vuV|K#7CzyQF;#JG4O721Qqum$NTwluebgmQ1cpH|<*BJMviH<#k9syXH*b__XBBYkB)OLLbOE zukCJSFHkVo?s*2gv@lP`&3C7TL|NDIL_3rQ9g!)aQk%Vqh$2{j3w{*e$s(1Xt04vP zbrjvDel=rwt#bgK=U$-MAirB$Ps1DZv^*e1>StLXnNYD7iEVE9U6R>K$3mdz^pEBG zD@5Egmaj={bTlDcPO00@UWyIwd=mLh-ON)y9#e)0-U#KnjDx@jWhiU zOJaWxqO~xul%B1Bl48D8&RP{)h8<0l?3^2S)+)`qaI@GfyfbLui_mW%ZRFveD zy)TJoTUC?fX%xk*(9+Dn7c;AFFcWDVliQ`pS-!K64?BVUq> zPjYZtjjH|P-8;T&e`;eV+TY)z4E&GRl*pnh_<9{}hsi-_)C2s{hilrCaW&_LobxHY ziiHm^zp4<0(m)NFk_7gMf#7!m3H7b282A{V& zS2tk8lg<;-O#r}CV$#-v-s2+E>bm5-iUo=NvNIr2kjzY*e_=k5X#9~maqI}4N09R4 zfF9V|tR|mZLOJlX!@rEjUwQtdGYWv_eCh}&uc?*4_@et6kBFvm%H-H-;zGoyM^&2ia%5)Cu(-y*GtJ- zh_Fw9wHzEcD2>=eD9?cL?>RG~n9PH$V33e^y^E#GUJk3pqVPh2e^yXijxH z%qxHOR(8_+*{t_A68knYbL=DPazZQi2OZF)UD5wi^i&!P%R&n~`%5MBNh4QEV0_<6@#q!;?(qa5ye*yif^+~xgz1;2D&Q<@QlKxNP-R$kx z#?sCLPJ}XMjUwc9GT~)uMo&8FKRAj;{DjT*M_Qd@KdN5VWs7|FNds?7UZE``$ohRf zWyQCRg={47?YG`R?>kbb?qL)i##U$6Mw;mS*$vV{y^evwFZGX5RNqreQ_ZLtceN)5 ze_Vy>3i&JUd_oS|Q(c-*D-6_KGhv3mk@?o0T2^|RJ$ClRZ#OlPQs&6Q{ki*kz2JQG z9^b2_5y)rpUm~X|N3<^#+~cM{M@_kvWGa)- z;rMS{CWbtnVz@IytJ?4mDS@>Uz4p$Of7Lf{wsVSHgf*D~xV-q$IX*2FOVn#~WQG)< zK6v!z(K1)a4-pW|{ObljqBNVnX1yxrTR`mRD}Dl~gr1IkhqM#Aj$qx^CF;I{_>mE2 z?aEK7ZDXq5Lc{)P!b`Uwh;0cab?2`pAWqP^3jO@1G+YH0_oiYTL28vbzQzxWe-VOy z#q=+8S9uuD4vIk=8TvMbOLf*x-PKPeztPzi%gS?e4wd%j{M~&n;=x7_ZNH_ z{n60DPOi;6NEP%xOFq2{W>?$phYC#833!Q^n%o1Qs}{%3daC3Vqq^;&KZ(RWg{e?GG{Pg)rk z>0G@3T>SmT^toGCwtYE|HTY}w9RU?lcIhOYMCK`bS2$XqINlKIb3*8K&_c_1qz`s) z0@V54DLq{cyFklk^l$SLBFQm+cL#`$MOq!}OA z3v_Ty#Yo2<13AC8W@=QKe`yrJh1$O9dYMl}HL>25BZ3_Kkd+TvQ&^8#QK~R7PpiTQ zg&Jh1tW25XZ&CHLoP6h7r<6~>^8v|ymCU2Ag*Ad_x=4P&(|<^|t#>uvUOVf-8Gv0P z@dvC-mg2qX$Ut`x*rFP2!jGCDP8Nr@c(hS?2nCyFR3;L0c>zC1e-bl3G`o~}zsCJZ z(dIdd)$))e>2-l?tf zd6BE*uX1F*w2Iq-xeq^w-gjAjY{pEi@UB^nB3-fYjU7Y&aVOI&R(hAWQJBVpqW3f7 zP@C`vB%34Z*pQnte>mq|x?Fz;g-=Adu~x~lEysX9wJ@$pTAZc8z+$>pJU?BZQ`TZx(;;p8(4mM zb?`bVGCPM3+m!~kIp^I234Aco5287loCfMLCGTa=ZAZ!qe_Fpc$S6pp8w|oTf!IE# zewV-%iBgD%J8PSFlD6E+J*5lH27tiDk>A&vEH4tQ1c_ zH*$!Iv;X;!f2U89C-Mq6}+O#o&K(cFKta>n^{oOyFOZ+ zE!&yw8(Gb>Qg^o-DX$WSS*G4I4*Q*4W`4UIwz=@Su23W!uL`waW{kZ4VLhdL(}MS4 z#PCp*Y{C%3?0p50r>k2v3e{8o5!p>cfZSJ8|=jAI@aGEc#3H7@h>j&*-uFkMOBLvT?)N&G)Re_~W17 z(i_u%ehEuKN>^pTGl=ciEx&?&Oal0sQ!=(e*f;h}!$t^J7nRi#Re=A0Ew;y)TYZev>~uh2!ygHWZd8C1~(-x5i~P8!jfK zmhE1!du;f%2>0H0)JS-MZlJsG=vdLulJQyH7R&d#!<(?C5;MPBTYDRQW}b68TP7A5 ze-}l@it5q{;pIag^tszv(ps_}zp`{Ls+YZFa-1kKi_TVZu#snD(khtUFSj`1^Ju^4 zhUtRdBQacD5bKA}yI$qAr(5Kxh_gdqYHH-gG$)BGMAc&qcI&W}q7R))*Gwe3Hq9yP zN7_EEWUo>?_-%f^;o;T!^z9SMz*_c!f7x31a?TEo{{EUQ?i(kKG>abW*}}@vtg{Lq z?H5gbK!!rK5@GEgM%_6klV`6_bJ~w$bL*vhLThnFae;?1rjOlCJ2w(E*tbjT_TIEF zv9ylPmTa5XY@N%Nw`w}DwZigEN;xIf7nbu)%wE`b-3}m16&Yn(xRZQuNSKvre{rJW zw&ZjR|5t_Kx(M6TsJ&dvMZ^pLR-dk`ilvMS~8_nU%cXF-za+)%7KXQ!c z$+mVd#Hp^^FWFd~S2)}`VO2Ap3P#qAbU{y9vOB%CoLvfCz?I>78D5jD>g$K-E~kAX zD&Br(bL|I5A9kKjfrDG>_8LAXe;`akgm?bnY7|W(KHSeg!Q?h4ZZLPWL#ynlKKQl3 z*Bzm~Ll5q{$Jlfig?+?!L{nt*o6}&ho@TO|3A5ru+ssW-q`t88MOTjTyBVk+V%#Ln`=L|ge2e+D&pw>zZo zEXxb#?iJN1E4th>GQ=9XqIU)3mRWOEq=vykVUw?=E*@3pKN}sd3v(?ffF}y|X7ImW z(efrP*E^M48PvhB!Xlj^s_{?y zFw&gGAfF(}CV*z?#c+_Df2{a{JJSDE7G^Q@Gm_nX5L9}xFF6aD8XdwaRPA+A2PEhH za(MaMu6x40XZ+IP%W_nW9wx5A+;1^&O=OE-s6PrF{^`J#a3nS_02m7#zyHuodNW?; z(hh*r+{G&1Y=$_~ROkL4zuU$5=g+0>{s*7HJmLyvZe(+Ga%Ev{m&=L*2?91YmoR7p z6c95vG72wDWo~D5Xfhx*IWm{`+48z z`8e3L_4s715SCCS1RTW&;unwr$ZO~siva`#g!lyn1c^8}^kFCm=-)6AhXEAn0z<$h z{ue+V34aBn&@lxt3XRi1zyWHm4gio403<2_5|a=R00;^QJopz7fs_C!fZbqL01bYC z8UhY=A>xonIC&yrHnu4AHvf7Ba6z~Mpa&1c?*A?a$T~uiFbEh9&;X-sp^oSsAz%l9 z9s&Y`qCEf6f=kjCg>sSr0^Qx+`N57Z{0O9tG=KMffIAFj3($qSK#^`xE5I+p08OwX z^ba$BA`XDQEzIRlTn}N5at9-!0Cd0s27$s|&=sz5D<~3x-W;H(st(X{g2MlR)&BtQ z1OA>30K^aace}sUzZ${dzl*^T2*S|`4EKb=Z2;CV2Pi;GNu3|%fw~U>!>xV+!4576 zbbmeA4GePtTcQEKCkF$RWOV>wG{L|0Tp&o86Uv3(1?KRJBJfum^eHRCt>h7oj!-zt zh3MCO3NR!Tf2=Mw5X0!mOd_KO%1zup1PB zLb^hIz5jROpDQ8|2w(++pa7Oo8yKAEKi$z`sP&&U`tp%54}hrvT6-XXz^~uG-=3o7 zWrct{c>V|e`+k9%dTNSV>b!p#|2roqhwuP+^MOPFd=ErG0FWU1Ato*e@cm~SZGSNA z?-&CAsa1trBLENnOp89Ie@*Q6R{&gp&A|=$XD>|zTD4FB*MCHACLkgJK|etM-%|fw z@_$qKuP*;Dk^lEVO0EtLztdd5^Z!R0>|fCd6>1NeVK4WWNbs{yrw zxjO!LRuu(C8$lLsgBCL%NSI$h_e$L?JtNu`upL6*5&UM6m6e>?yCrgAgq4*Oi)A=07fFgo$qc6Z3fh78Mt!SwOoscjzeRP=UAFUy-NF>@~zkd~g-uGYY z-+qQdJ)jVx=}83SVTfI2NaIDhETcQ$*CD*c<>ttE|YFfn6+gEE~KdBqA(P@R^<-)^7<}M+H9aKRtN^05wXB5OmR%_B^gfs2o-A=+JAVrUhuQoWy9pb4!{vm^o@)yg%K4io72356>ZGMzKrTO zHY1CpK$@nx>Z?8(`H2+`i#wNVjG@2uTs4hL@1LLV&Fbz-6*_+PxZ}NOSr{cfm6sY@ z&Ol-hJ#|8sTCEEPr<}+)`8Ub&cjyZp!#EeP9V;h5Oib)?o%8~ZfPZ2py8&5P1ZE;q z>$ciRbwlImpde?jB-;-!PrR2LQbV^Ik^0G6U@UO27_5v7vgKLapU}MCd=#l%{dI9F z*=;yr2W-rq9i`WENH?(8&Ly9_U=;0qsB%kF>ndt#qb)t&sX+=h{FGJaoruDy$&B8t zdL@aEj_5ozrr@*f$$z?;JpFSD3Oe~a4o61oQyI!fI~$mRlX4ya@)!6_JBFFK@9$wK zZkR4>SX`-S97havh`!;4P%c}}IOQywSk@?hB5(1}#yuD?N6ZPYRc{UVza)uKF4tQU zm@~UJ2hN>*I#yz2c%`1@hXCS-Jrrbwe3Xt5<6qyCvuxIl!GEgoUikVpKoPT&SdXfc zzxn|!jz2~JJTMmDz3!MXLU~k@;cm@V4$yQwUHD5wv7J|wm-Esn%Y7PEyyU`~fuNau z8n5m59?RE+n3idL-!)a9Q_v>S^dC@wi)F~=c?zS+=KV2#OumL;RZk}0zrMF!?wWR- zc_UNIwUh5jL4TQ0SG72P31oNL4b67xnnR96ujRb8ApgOeH4WLmM?A5Ian#>E%e)nQe>Dq|CmOeb%zy~pK+lomkwF@!(D zk`o*InNwZXC|%xwh!hJ7z1qMV)NFWPg~@TYGsO%S1iyXC$VYroUp3Futr& zLja?kkNXG7T3#H4;l!*D5ghOHpMb27TFyU)kY*>UQOS{yyWbP8`w%GelgwA)7?{rK zPZH8=B4v=8l3cVh^mbLOR?%M)>g{9t5d`hH0`#r~XpQQuEEIh(dm9mkmz1AYr6MBS zIe#O($x8`{^)8>BPIZE*0}B24nbS}6E*wfTbcr8BipmeuwS>8HNqe@Qe00Q(um8+* zON_wzF|hM(>_g&1oE&nl1U{-TaUlszQAtT)NN0MxF>B7{&nAgVTAZpK1^Af~O5!eV ze^}Rp`fSeDTn4#Oi9UA{BK`dOD8slthJS9{H<$Q|B4X-Gb}Jc{V=v8pR$(&Mqgxy= z*vdfTYZr^g_YM^bXjl{IWhPc`)rfph=tp#a3d8??pKS^gAVioXs)EC733<_Lwa~q@q6@M?w z`(k6>`vxrZXVHH9qHsI1Z!h=T1}^HH@n$Ws=k3lcLFiUz%iu2Cq|pOW#e0%lEXmvL zjs%?C`*CW`WJ7xHG{S5`!_~WcrOQJfnM#&&s)lg}N2)@@QVr_2)l$AX-w%#^QfAT- zGH;rx_PI&tF+E{S4v%`O`1_U6s(+w8ZSZ}1%atmg=`Vk^KS-v0 zo+6l*J(k%p726$tSavsxb&w^yZ_C57>shdI;3LA4z8pBhJ7;2?rh5&1?)KdX7GDu+ z&z{{&Q4Yh$mBPO#|6=(&j<({S%k6AV0_nT!85)B|qGgJMY~%?lry>MfoPTl<-V1&v z7tjIM$3%vT2;B!H+9t2{7FAttGN4kD;?>s=n^ zNjAajdEpWaz4WhlN+~_`G=GkZln1OB^G}1?cq677Z@s9qV&IPzKxS$f(8Ue!> z#(F4rw!Vj9G)%vX{lQW>vL^s4jJWbRJ9K4#x_}dNuM9f*Bt(S7tt1yl@&q0#m=R&t zL0#S=C3GaQ z8|7bOH;q#Z&rgmZmDf$9XAV|R{0xu!CrB7Dg{&ky$lir3K9hD1FjjGVxc$^ejGi|eT1*22y}s; z7mXIU*)ca&Kc}wn5hG|#67Bu6ITT3~B)$*Hl2JUuDq0)iVB_4)9etk?nk-~3o;&Gq zwPh9jLqj){Z|lCm8>LG)7Bh7g#y#V2%I>2(uKOK$7Cf;xmVaw3RYth)+{>h7jh&@` znmv@upY9qYtBxn-8PRsxkT~BaY)h$aF7dRF-*{>CtQe;|HOh#&v9f^MJJs3J)#?(F zd4q2wN*yppv)mQRZ9FV3`iX7Fh;;3#k3qlVH+)>nQcsYXpw#X7&C-%~r{IK73?+|F zC+XJZ5Mfe^jeo382CO1;9P$;087&>3ZXJ>7zkx?opYS-72nsi-b0+#0q>C5~0)Db% zfw~1UzbfNmvQVFz@0d)a++V9M_KbHuZ%o{N@~v<}UP+NJAI^oPujASO3chw`84V*tpfN1 zR&T%U-2No>MsTfgByX>-rPH~tmm=bQwDRkqbme9dGF(a7`m>!W)e=p|1wX^e)u2~q z!E{L7JAcfwdGD^*pOVs)n*D&!HwZ$>**K4THz^`^Y9cv4nZXnqeg>RG9}RrV^d3jD zug4zWE}Mj#VFhG5y3kSFqw-Ni4`!ZT?#rCTXM0-E275q233koM$9CVSR^=MbXVtk9TV2k+HM~) zz3$W`Da%QsTplIuu%D1mn`bFiyJ4!>A|}~Xw_#e+9Mbx!v27+;)tF6BxMIUHz$=;P z+bOeZ=Tk)Ra3xF2ihi|pW+rs0PF-=XS9wu(h8>;v zFxc*e+bCV1_x}0rqWA13U${N5iWb=cZu#O*!=a*^sAH``31vZ=1J4-gch03;xbq8# z4;X~;aL1Ur?3;1rPOhTNYZ1C*-}V*;O@V$DO!EGM^CXqWo7j6C_JkTW!GBBMaWziC zUohyHLP|gTZ-_jCy4S`q$y_owo@Y*L`P5ZDTnlVYY3&JH@>H7L-3A@LB|X#xPB9+a z78%(E?Ks%FCo$-2)U4FDn^QAqr}vxpoE$qsu~c5RreSSjf4 zBFNSnF@V#aw6vWGq_g+*6DwfllE$y$K=T>KXG1ki3L$THczfFqR6WA4zN{uCMmEQA zX&o%GW5>yc8L0{DZ+{qPzP-cA&lJpT?ETFd8D`h?epG(uE|Rl_VdYkF=tp(Lj%q1m zqk=}(DUe>n-Hfk}2y@{KrsNg>xNXTC42GOufK8t|qtvy6V`3jtftU!k)89!+@RKp0 zb7~yGu}~8&A(2z?;MZbNvCYvx_|I<_v2bJ1cv)nI(tU4}et+FmCGo>EQJoyN4ORWX zFt2pu;iPkxe7^>!q51k~;~MfZgX(yI(Z;Z9mGW_-B+E6~QPZoN?I)dOT1e*2uY6H_ z7YDCUL^+Udj^}}f*t#-e+By`$jbDX-rhm}IjV9E%6b&k9nr4~s!U&}cu9`5%;gV@v z`JhR!2BUNjJAe4`K5h*7L2vzRam^Yxo8Wkg?DadMU2?zmgtL3OR6a|!$(kZ}C=dKd zdI5s;)QKOCRRY7Pd8RM(R^z@B07?9SH{LuknC1b?^L559hk3gw|A5`X(vHXdvdH?qx>90ID3(=C*m z1qIq&CY6uvf0(;te}mux=a=R7YuTPepS%@s6sSJDh2cjD7WuK};+YUhi@4V>w z+uN_ApPk8H>YZs*dT{^lRS;Flfs!QFbX&_)I`IRncO&p;c^i@wnqxp;lUsUt8h_k}OM(VGZwMGet{e{IUMBI? zho`;EVOKX=?a}tfY&K59&Jkw~7I1v}i7yI<%}omp*At9E6lc~OJG{ztHTAhAfMaLY zJSSO}_&qy9(zFs6BYpP~srxZ^r+7!;AtI|~jy!;HriF`pPlek?YO~FI@nZBtZv9gk zMSnki|C&;+v+JX2qX>$7)ltv%uUb}?`%_=%-{~Oim3>4H77x&4*V|a;TYXZ{*ug=x z6A5EmYrV?X4mR*iHKC#+-@pfHd?o930NhRNxs(;;QNgfpj3O`CYvC^M89S}3|o&Zj6byln} z*lF=;ij-dBVj5H^DL1cXHzl|1UKb}v@%TTU{oa`$Zq{SJ#$Ud7M!L-GP+(N02U+z& zUOzW|N;Z_X|6PYOyfm6^2K=hm=pCvo>#XU>6q)guNjI?GLbztSOLvUeJ3ony^?wtG z5XC$VKW(t@BGD9|Tt9EfV8=rG&FYKq-LRRTfh_(VexHNuB{jr81WbqZI=pD#n;4N7 za#_Q0kr?#N@onl98szqb<2~1ZD=yqXiCwzpC)A}cO7^=J2iT_pv;#2DV}iLi(V zP-P| zptPkutpk=j2|-nWs#&sc`?z6;?e=%S*F7PAiv6sN6G3;#HE1u?B#j52aDQlw!A)b? zy%?2>40?>yM6?Lp7kX49`^l#YOPO~;UoLtpTX+3%DwtGAYw}2TRj$sVebjs1#o-R+ z@Db=-46r(E-#^A+kpNvA<)gTX*9p@sRno}}_)L$PN%UerXdk-iuel1btBkOs<6_Bq^>UvCM=~iqccm zs)7yT^Hut!)?X%1!qth-<&*A?+GY^B%bgJ1?kcfLM~-~)xv2Ke#^}}mDw3yzm=|o` zYu?G0(JtO_ghqS{$(iNn8yG45`qZJayQS~AOg!&KI0J*4>RNJG4S#EgPq}!s^N|-p z%TeR-Kn^E^Jpaq5kzisTwPr~2fyQq+-lpj8HP?T=eL(yje^QE8hS%ju)jnwl)s7}?tf+-QNf|DrbAvj8_bmS zoUO-X7BljaltF-@%YSQ5)`H;}kV5Ewf^oZ#fyj_t_HLfl#72eLnz?iVM53cHLGN^e zpi~!&YwP+5XKyQJpMpIO<$EN_LZpIw1&Amb#`CN-mbT_kIqV@zJbI(svl3cL^^w)? z4WaK_YF@HJ(~l{zPNL8o4& zM0f;a3cV6ow0|o$mC?uE<;KUpLP0ka(pUSU2lE>w#NCfm4|QzM97u}4bloHsv1FST zH*|*4M{efwo0~&?shRj)ic1Qam&_-#l%;QE&MU6>2MpB)SI>-~r^KTvV>mJ1JjXlm zZV&1)ul?6!|Shvdp&1MP&&Y+;~S4>`z`ju&wn?trgQq-oF7}PWVc>Q@-3Ck z+Shpptq0^k4B*@8#4dVcJ(E6N&@HwcdO@YHa8CXC>mn(VQvBuAM=vC%Y;qv!%h$La zQx5rs)EqvG?vR8VRfuY_VjV`+&;%~VyZ{wcap;!RT*lzjGD*HR``E@ddvf7#R$D=z z(>9B>s(;u{lFjU){(sZ+G3^EQ;75!)Lg9;4nsRV9YAv!BT6YZ)t{BH3Z3?ES9qyl`+d zY0It~M!&D#cCoDP-z4@yIxAXLr$r-JcAfz)o(Eq15AqpOu9v}z0uur@IF~SJ0~HW9 zI0`RJWo~D5Xfhx+H!?Jr@u34112-`_mw~1NEPqsQ_pX#6h?H~<0)j|)NlT}+^}JbT>+OhX~S=0@5lXN_R-7p#L$x@B9D0d)K;mowd$6@3Wu1-@V`c?3a^G z-;hrcjIe{KBH$Q4A$~z=fU=IExfDQ9P=sGlP?(sL(+G-zLH@$ToF)(y8j65R|1W|v z3V#B`U@;XS2FueyzyX?WFo2Kou4lvZv2IwIn@IPejKLlRD-?IS- z@eBPs++XisfuQi;&Oi_d;erIhy`XRhfISoj0qCh}^J6?QyZ|5^{EG;Lp%GYrpnp3M z3Ip0<3BNN30#p?Z06=Vkf6GIIP*5ZW&5wq{ew8TjD-8COAHu=P2p1O!9D^qQm7fX} z1p#4?-Amw4XPw~)54g|Yg*_Aww*OTG*bOOQ42Qb9K{QnUQm_#5e=r9K1|TLVB`z)| z2!OZ(Af6ycfnVW`ypWLJMxkFYwtoUYA0z?^u*Vhw@q^k!upeR{G|(Laz@Xe9em?)( z@Xr;okPrY21z`Yo5C{T7yub)J%>xI68is^`fr#2o5FuZ z`G1N0zY|h*gTa2AxqsXL4>QmO3iJ9)z>3xlgB<`J1a=JI|4lW8{25vu2pH<-^50es z3=lgAif{+4nE8Z6`2|J);D1oGD%2AK)`wz1j(;Tc2RHsTYA`4qqK`mBf88xuFG0co zvSH^7HPNzhEIj zVSs=g3J8M0Aol+o7WoVRlP)X@5I{q*Ck#aW#{Q8y$PIiv$C>wriDB+e<@h*i6K1GQxGLF#E9r((^*@z{ zj(_xJA7gN3U=rp)sdQtx@#nUP~4+>4RUtzJ1=;n<{dUr`268UVj-%MSt5{lPzl~p`V^Q z{<&r&`r2J3u@mLeBML`$*X)mnmH%zH?odfUib~ zlqPo6%6M*YWb&2Qq%{Rjb!1mB1@+u?qxR^sf^pflQe;&y7rT7&%pv zGPlouI~{Lai!PG;3MfBVdR5s**aAY0U{Sa9 zvsRnfQtCicCxxU%#e5PI%C5JyK+~aCZtA!@rWN zk-4Q8tNa=`O(&{Zn5bWy$7)^CybE(56@PzU{z_hxCRnkU^-!TSveEF0RYzz(vlQg7 zxB^O?JTu771p{h0HKX=YDXGK~>n0_bP$6sVBRPAsj+4?&tW9(!BB1fv*cy}KTcj3M z6jL01R$FINUvBu_6EU>6Lwf1_oL(}08+l2RG{K#(CTz0YRL27++A}Vr&x%RZ9DnML zqHF(5RWWjPE#o#+g{5?2-bh+B4R(SEaq!GI9%rSWie+B3&496pSBZ1*JMe{ zz-_D4#5wQSsL!jt&5l=0BFsKDL4Q@n_P{Hv|An;u&jmqVUaGYAC^4(1eG>m{gla9U z=rOr#ML!>$);p|3s@j7(18@t@@S}k8aojbfey^ejKdNSruj@v)&D(i*X@Paweq`zx z%RDpsYQ=Ouo}KJW&ucEs*+2)pF}VtYKWj#C>~0RTUe60#nO<>StxeB~EPp&svfMj; z&DK-^U6CO<^_O#pA4KQAb^j_Jk93u3c%-DPX zXrsB0!Q6iXMINm|y2yA?7=PeyK6@D`w(m7=S#mHp@4MZFNENYk>Re_q5_|gmh+jY@ zfACSsC~9HbtG(@Q^bqdc+?{tLfwLFH4aVAIHRcSxO*$&!Y`KLNxvGOM9=B|K#b+gg zgRA7W-yH;LuC2fIuh=m&3pOTEQeu7jx~N^>^v0RiE6ozq^bj$1_J1P{DTmtrJDEty zl21|I-YWrA8lxpghpJ6>29Y159jA@82a;xkFVoISRiz-lYxJM>UogwUk5IK%I(gJ6n_m9hN#o-9)R%mADCaMLrp5}&pJk_<~JS$IU)C@%({}2CwRK$ zfw=aN$COsMQzHHEHKL^y8p!p}7~dp5D${i6w06ePLLbx--`O(qhfN=3WE?&Fa^b+h zRWIuqm`TB*hm#lF(Wv=(ElctqSbd6FL?Dx-OiWWIrHlF@Ie#;nhq_tE25m+*{918M zlh0a=hR=_9%BKcNM>1c(^_m`0VMjWRwu6|GQERsm-b6mNS5zBj(9CMBj zuy+$&@a5v7GJgtHm3|eG0!rzw^~`r!Pfr*+rt#FkbPXH>2&L5c4 zu6Ge7>(XFZE|Z2>bhf6mt5S#?RhItZSW`KNvoC$xnJ)`-sZe_$6lIJi=i>y4lcxiGL@QR9)9uQi6n2lsx0Dlk~Ej zV1W_YdFt800mX=4Msvhr0qI#lnHP5`hNRG9ymi=%uME%8BZA0nl)&Tp47>LS-Z0Ea zv8CK8=W+RHCzVM~s_}f3^Ja-^ChTKJ;QkDEkxi4UY}X?ZKW~)=1!)_%q?)z(r3mJ8 z4!7o`$$wh^eR5X+OunBi_v+q0*$y2Uw)PaKbMDgWy!uQlbZMQ{o}8MIL*m?r)&4~hSpCkd znSYaW%nXHaV7%>vu$iBV1E*bK=FzkZ-^KL@fk4KzFchCS%@y5 zJu@+UHW8pn7?`aXOUjAdVJ9OZelvY;W0oECkYSA7gPv|nR0hpVKQgI;7poQ;G!VYz zmGE}(DXkSkgy`;YP)+YyC~ZuKeOC@Fl2{Lf}wlw zn4X^Z%jZ&yj7Rw*$LaG?_ z1KI+f2ajr=1Q*!qKK1vHWM>y~H#oHqx^|^ERy7sRb9`Abnlj?mMP%F}hhgCrX-3TI zHN*`@p&^AuT~7mUf|l*-7Nz}rT#M2#cy$CN*8{_2X=?@u1NP-7HT$x6TY{N?fR0md zUv@V+Q_KqZ71 zb^yy^q}8>NAhs&7Q#M8cv>s8%zh}01wK=8m_LEbBGC8gD#$>H|kbfUr?n3$4*cfcR ze&qYB0$DdYG4Xp^a1_;GkIePt^Y<6S8XuazP>nG=o8Gok`7uo~roM7<9Q#8+lj!>D zWNJPlI1DX=xY^CuLn`aDQtdvD8?=>$MB?=qj7PM(iA&TpI0_iVClBt0%*o?*E5Pci z(!b!->Xb}uceutWhkq4P-U8binv>0znW@TNX?gRP3WCSxaJI1i++I@|k&R~+V-Fv^;G6WJC&ABzG3sYSv}LJMmgpvNvm;`3US(E34$(JR?v2dEgmC{2yVvF6o>7Z9$(Ozb zloZnJOJvtR!_;)Q&*U@GGaJXGHhjR}8K>5T7v2_pLFRsc^Nw3w{^gHtd?~Z)&`~

P1$iw(edi)G89jS*)(80Hls{tY}YN$0XPwzV3W)L$vI{O*T|tD zNwFsoM}$h@Y$kdqWd=NTX_M(Zw=}5KKpGdmNPIj)aIV1Qh!Vp+1dWQx}nbfQT;4t z@OGHnrr%{qW^8s+sb0;u$T*5O(rO~h_aDumluV6qtlXS0$HVP~cd2L`I1EXBEF3>m ze0c5b-6;|z^*tXN^|_$@LheK{aa&Mx5A{Vn`F{xP|r(U%ke1)e6KFq*RvTt~ZFgk`MGz5_rCI^JsH@ z?he{^`B)Oht@w>_a^vj$rJM%&tH-Ys@_8iA6Q2V0EfNVYe%yex1$TuSM;m&2?9Kwx zsefr_jXx-k^Eq#Hv&t=<92Aji81NOZKc6@Uq4$&%rB-42LUAt_ITJ6rlUoCITPby| zO{Q9vUmNX<_TB4&pF;O`9K)!-5%tT#r2@P(;nvitJ8A(!Hzah~itMxHAOZaQ%aDU`AwROvdP*XaJm?5Q{@lV29G7SfaN?e}E zanjsd%UVsc!08LC>Vd6MZ^KoZ3gL)1h3W`7hi(K%pi-R&0pEQwh3g;UAchrUw#9NM zL#etZEOT7KT~QJOQmUbPi#RXBIQb5zyw0Bcu$bJK&R4I5tj5z}dVd_g zjDFV-+fvRdZOx-Sx=Uk>84BqooNN@T$4hIaYG;tf&vuv?9Fgb0qWd;(c;Vtq7d-YWPh6zZf!K~9HIeP^k(UlIox(W|w z)t$^1as+^mzujX>5^eoXPk%q^s+e9&csG*dh5j0|cANaG{WE5*J5a8l7Pjw&Lbk)w zB7&_g680K@d`_Z`pO50L?5=q~$wYhvi-aXrK7e#EkYjRcgaE#0?aEgxBmB zh{8iZrf|*StHO^naV^z$*DvVqfEK*eMZg*79b`)?H6N47Hkrvyynj-A_^PL45D;w(yhL z>k#~W5TEPuojW>TuYXsBQKe@`c&&)EST2}+%ULkFrbe0Kbo1u-x00QKUneAj5=L9W z)pv{eEM>~)tHE1^yJj-s_Z7{H!lv4ZJj^k}yRh}gP)Qt1(dz~R#Sg?gmYqM}Ek5~7 zMPI)Rgi4Pa8w9=hMlzWt(Uiv+Rf)SW4BV<@2-zN;K$HS9>VNgAAGLNzP9P%OBcdve zEV~LD+MP{fp5h!Yv~ME=wu~!{a;E%CNr(3XLL;_zWGMW!B9b?l1=nnx&{VJj2l3d4 zJyf{GpU?C<(&;iXI;{r<3m;GGwq^J`Ai;kZd}{u}*rAZd?>iUW4QKKc+5|B3#8KHQU#{EE-jPqQG!c}I zh-1fhf8dh*)@H&8AtXMo#`Sckm9>zAHu7_@j&o3B{%2ZauGQR>P-T>oOW&b$9h&^}{;ipTPp^Y=R_hmK`f0w$OOJoB^*+bpcAmmhRU%g) zwROHQy+1c2>VhwNI^zND8xe)?&Yw2SXqx8<*ZJ6j(Q-`opG z`{}~ZR52voQ6fPlq<0<8SXp7sFfv+*e^83=`vUOolsJFqMOz$6D}S2QDlQa3F~4~^ z7ypiikpy-xu^xZ(Q=dDwd2M>#oL*>#2v-_V5oyRUPr|fZu=j;B8Xy+IP6eBP$4#`d zNV?uxmF`EI+S)nZ?-Y|vkgi!l)L(R%CNEJl6VS>KoaK}W)b?HXPgZ+7_XeI?lvDHW z&4crudVBXEbanLQ$+J*SGb!U;GA)XJNQC)-g_^8+U5S6!kB~*WTN!|7V(Ii#1;H1% zvLuuzCIX>#Yf0k4HmG|Q$D!IdV-ba@&by%J{#R0x#?qUw@cXWA5^LuS7{X*imR(DB z8fxnf$V6&cCTkPjU6id<-vrCo6#&YvCD2nxO{@TWO|LuCgk~r;i-z7_3L!&tCwi=# zQ&3iAm}-ANJN3yr_6fOHQE`VeX&MA@UXoAJBfPEFAJGQjeRCmRr}ki%zKGA;r}z?2 zS2N^1La=}_hR$f^2u$PM9~0hPv1AH~wc6c$EMRh9Ug)~qa=agYq~y7GM~lS=SlMzN zj^j0RtEF4=rceo+ILs#6Oe`6W}yKy4^?Zf6d(D)H}a`& z&CMabsxV1Ywc2mll;@FWKx-o+Ht(ilrqyl6Io)U9kU!h(r5>LUf20tVEa!#gOIgj2 zDqMdqL0+W}->P48Bq#tADoTuexWQAhdxx6J+#0MYWX5Ep4%iAXm%W_a`qt;sPx8~B zLy|?g$xMiM9a?W3onvQIFU{4LLD^o=+UQz-@D^H~T|n1nf{c5r+YZB+jOUC8tJ7vc zy&{A9NOF#=0}oTVbL{J30ZPjJ|ovpBu$NLD)Lu``9RXi;M2mV<5cz+3TGffPP0CIvedcl50L&Ec&bV64{P45+UChCJ#DUBp=R(@a2l%IEGBkQNnK1v zm+kgB-%$5@*)<8S2xhnakZE03l~eWtb}y=_%)nJ(K7P-COE7!Q@afS`sB_-9W3zkH zg@xYaZl#T3SSOIj%W*`ve$vdLig~Y9c|X;!UuxqPC#*f5%Pp$)6E*BGZJ4VA<4%1m z;L-m8|HJo!m%)kx69P9dmoR7p6cIK#G72wDWo~D5Xfhx%FfuroA-@9_1Tix>Fqh%R z0w{mC1yG#X7A=eg3+@DK+}+(>g9HoOG|*T>;}%?k1a}V-G`JJoo!|r~I0Q*>dt_$r z%-s9`Rqs_-b@$qPZ98Y}uj-?v)X-v;w1Aj{A^0C61S$lOF>?c3094rkN)QK-Gb*h##L*K9wz774uJfP200tl`v094IftU>nA6@g}U04)d*407@OuM`X-)-EoNLhS7B?(S@6_Ree&sFi;h zBMZPC>|zbj1UZACZXgT5?~ws&X7-@JMq@*z1!!A?o&R)bK`dR|&7dH_v%wAw1UWcA zhqyXefS`cq>HsZ86@a=U$l*`2%AW)lz+Z<0;AG?cJKbN=zY~ES{s=Y$0wMN}W)7ZU z2P=Rj*bW3xms4SL@o-@Qm^oPdCYpcQIYXY~&D_kuc4p?!gg;t01IS6f2ADlh_}6^S zKq%PJ#hJ|+Z1;Oc_TOoqk6G5iLKXRmKA*cM`(Kx9>i}_g@cs|7 z1Upz*{vN}^)sbDt0qo=oQk40N^K3%>$7Tg`0q}Bga0qa70YFXwkO$D3{da!>ZBIwg zA5PBS=I0rFy&WNr0L$kwK)zs0(DM(fx3ifW2;c&B1^Iga+wtEBm6H=-0S3AN%t2OQ z2h@MEKbt|8f8yu+hk`u-h8)lG;{$j*6-j^Is$X z?Ua&&cmTXvc>t_j+}r?OZhn6NpCBK=_rJ4fn1TQ5;$M-94wevr;GfMtpVEICcKb^K z41cW$BjCStsX?A~3j#3wBlEW$yd1#iFV6pGvHubB|EKZaS^lpw|KE<}T+15n1FDecO>p>MsxIiy#Z?7afL-nX*Q@Aa_Pl=!k`7jOe;*{+ zSq|(0vd{p#0ImPf#-C=L-+N{Ub^vKWoWZ}JEC5zc4vzovJuer~_W9}I{4B{oT%hOO z`R|Id4nT;-?Op=O?_9M4MP;^hT+b3SjQ1<2zM5d+xS93U>wA%N%Ud;yja zDC+Mk<>v;lEB-e9i3EQE?CO6b0WJW$=HEz=AHZ(*4-x>doBxBJr1wYdilF@Lby2&dk~RUxeozF8_am&)vBF0srf2fUZ#J z^YiJC?Rmbp|KL9!h9Hm!2#C5k2LTEP+tdWNT~|qxxU+7Li%!#S>!vcYdM`r1xZYwQ zB{5cJ2ChLbC6k7_v6i=F87?H2C?32Io9mFmS`uEj-uc{{#A!}#x1ugg67)|L97@*q zk)e>XYD?_+JUD;(ya}*{Z-(tqqRn-36~NH=hl6%lp!= ztkF7AZv#Hf(B_$E%@R=|y0DTVGvSi9$UKBO`QoV}lK$Ee==3SD$taCj$p@B!*;K_OiJ_aEU0@f zQ*=oq)GqKM6nOR7m$pOHV8L_PW3V!+CaiiOK$lL&Fm8GuCW8*Ae!FE>m`aeeHAKVr#|)M_&E#P`I(KPAfuerJVJF)7;JV*EW@uWqahl1)zG;?VNB?sQ-l*8oGCCV{ z8@D{x(bAhkrqhQ{qHH&3KTFu?fW5Na+Gl@alI{AFrX<4ZP?howynUwKrSUSI?y^5& z{^2Yb2RS1TuFWLb479VAP$X+zwxa?0-%2|^3`B?z?nC6`t2)HN_7*n~f< z#6~F$GlN6*sDQ^8MEuzUhbK{X| zkqnN;AJwA2&@_Supb>%=lJQh)JcEIVU#5y;!PQ@OnrgNHP(zp zO82K+(ejK;-m#x;e#|5d1wU-~yJ&w8!4gj?1e>^n*o?%mQ{f}p>@br~;jeU-C*}65 zuVR}y6J2B{0>3t{e3U?+`sh!{N!*iPBWJ8Bp3It+ly%-+rB{Ckyy0>{ zKT!vpcCO7Yvxsv*yuDa=PPFLz>(M=KfAbtgqp-|~F3+YKp%0_q(JkllN|p~I6Sfb| zitdUnt&V6r9P{o(z-JWd$EH0&j32AOl;%ny654ClvZ00}6UR70Xbsdz)8C_{=W|T@7 zN+TFo@;(Ss|B^i3U=|;cI;7cM9$-9>h_4a)#Gp&R!vY@$^C;CPUy6T5%nFSk)y@k| znkCQ&`y*fmOR?By!58n+Vfl@h$x>ywp0zIOuiuuKo`_*j8Jm*B)G;JgOw%LUr`?jh zRHnyN-ke6K7t6uUlwH?QV-Zr_^N!HsmgmyMkl=fpvo6khI%`az7ftW~ZSTTQj)2X{ zI<@D;l|D(%`JG*$S!sW<`{<(DXC23}Po@_*epj!`UgP2XTvMEt%ek$9RR7EQ6##uPxnuYm27@R zBlX)C+NiXqbVsD#!3;#oeL`%~N8mAK!H*pt3Vp1&Zkw|AGdvz9yyW5AF}aH^_Yvv6 zEWffpS!~+z(wmQ%uQ;(JD_pVx)Dx=q1o}2m)YKfV40`3yUm!WTj*&MGRL`jjEE}Uh zzGSA0DRas=CG})NvfJFBD3Jr5+)BncBH2~Cc{+s!^jU-0{x%;Hw|Tlxw4-{7!50WL3t>Wp=0k#xTXk~A7U@#sv?X25<87?LN@7^HhUitLkb2yCo{yENv=t_17>W#Q8E* zf7VQ(3ET0QbibF>q6p8DNHr*)k(Cf|xn(YuGci7RKQG|3{7rr$dv)dHt#FG18mdG8 z1|pCve67-hLT?-qFPD>2TxcDC1G_nS8;ih&n!IP7K%BOD>MF@9I6|ufWaxjDo&y?# z3GoFCuURvu+{zrG!P~Ijh9C#`9;pZOxm|d<7Q{-J2cbmvp@2Q=fc-0`&~G?&(9T^_ zbPUC#14Nt{;+Umo6ESS$0CY~R11*aezhH>A^=W>Dm@*c0S6oc>63q*}h#>gUk%VpZ z@>&zeXi`q`v$n0rX@-rJdA5H-f(0@ggAi)h=1Ey5DO=PeTncT0qf4W zQGufSdy86jHk8xQ1LUsze3>qSXm6MzF>r26Z(S^$KN4g}!>V}!8(M$UKiviL%D)@) zTj*}e1_2JZBFSbf%1CiwBesmpoxSKg28FgK%ycE|O4G!6h?r3y;EH%%*F=@bwa;%W z5){JmVFilyHyQ~Xns6s4yzi@712EgJZ;`WIp1%5O+#e;n^x}W~q~uYXBrrcXtx5WA zgN^X5_pqt!%99~}I0=76{ik2F`_0^4;W76i{M~P7p$>teYDyIK2ak~x=V=eQauv4a zH7{#loVTmnbfkQF^?7Y+hq(?(7xZd&(z!%+_DxD8W9fBj^3Seyq*A3b{WoVL83J`j zot#|{h!)gJDx3u3xT3m<3dhyz1_r>!L9)k}bvQ3d&$~ z?VzSe%~G0amO}Et;v@FlJt>%iqs;(1*;GVDVUR5pO=${UFXotRvyk+^32mBPghutUoy2z`#H+frKih ze9-Np>%edJ(KTSW2|k!qP(c}z*a;iww@~lN-x&^e(v1u?i#sQ-T&^I@7IG$}MRFr& zqEZzZMI?Vu_1vG|v=LNOeETA`pEufib_J95Z9>G&cNKE;8T>AmN-K2-dI9{Q`@zzR z&y<9(WM@L@g~=X6I0*n@TcMf5e)PmY%?$bhcDtm-)8&`^H&l7!=Kk7WC6cLLlO(!3 zj@xA5C(K&wabDF5(_a)%zNj-Ua>5juU^w~dciVr(E8qDt7$ksY5^FYY{E~#i1jQnS z*{)2R%9|X_S7qV`eWU)3;A1E@@`IGsddnr6?>XTYhIPw)N%i`4wv}3mGlq1 z*8ydC)Q+gW`ie-(hJiPI;VkXbSCXkGni``NNE)_aktF~#+Aqh*X$Pg2wsX~T(Q3qBa3rQU5eW@FFRk9lB$}xq zZi|@(;6U4W<`ZTmh;J;~OH;ZMcik#^@oM1?S~7V-_`Ncgn#_$rQ4KKMlXKZOy*L4~ z$Ml&=d;3WL^l!ql6_qLny+)Y|J{4LC-Bo|pVT}Oli@^@0j!&o8BV=`-ws)&KTl+Cf z?RGIPYDjL8~b5j&@#f*)NSl-#iX$Wr>+R#>SBh)amoz69EN+GeTa z;ep{fWfO3g_!%=+IYq5AkEeP?Cd@90oOxV~l5-f|YsJ?mTfm_Cs@%A;VlNG**CT%= zDO&TI+ZId|yLM{FgbOj|xHdy!C3tY|UtCT4KEV@_a;yC-jL*$sJ;I0I8;lhBWwttEVP3X_S_ zf^+uJ7d}1w&>Ebd@;9_XVdqNF#|D3qRfaMvdE@Thy4rV@9vb_I#{SBit24xeUF-U{ z`XMqS2~^iji8OfPmW}-6DNE^VzSzz2dL!Q_nGvY%-SI!AO4GXIUd)7a_#YR zh{FkhBs^Et(Ua6f@D{Q)462}-3pO!Xe28FRt1B3|<2?3V$=@9nXyDV(h4+7gMtab2 ziX0p<4>}Ec`IYJl=B{SFyBY9Bw%63@6?=b06||hqv(}`&@UO{KV#xz99iQX|D=Z z^k&rgcJJ7+=;2I-C?;mkzQEOW)WsE(x4`0`30Orb!qQyGvm`x@s+xNG${!SE5)O+q z0^^roto}?+f8yUzen}}HHT>m+9qbU>G8!D@f_#FYe%3YI4OO`@SWAC=jKe^rl&tMs z)i@g>3%4AI?Pt@;*BS0C*gKd2m zsk$vK{p;iV&xsctU#Xz!B+eP*TGF3);6%H>dJd!A8P43z7>iJ{7ID&4@v0=Qs$yd+ zvWh(t%qA-SfZ+_Ypw7fm68u4zrWgYsLR}4*=(GB;)&OK`mBN39WiGD3UzXE#un1N4 zozT>GO*7YkZw#z(OWy^Gn^>vZ@)|&TAuD*_NsDu8gT7kkhQhxiTsxNU7Mri6o*gESuiyVS7SGIMEr!gJHENk#RY~hzI7QvVPH4xmDutT zmM&U@F*1YlZOwn`U7!c+HnG69Nk>M&z^D^kzJGo6Uf;BM*HplS z8l~_M+xf%*Ne5GJhl=V|`M}9;hIvO~k*YFrikY`LL^(ro$6-n*7}7-IK5IpG&K=)1 z8|@RP+3rLpj(u82e_MjN1FLmA2T`1R^R^f1n-O?U+iHI>GTP8&&7j`o$R?EZbN*u@ zZ$V`-y_$eKrfN7X60@w3TNG-*7x$`>MNsOiUm4U-BSx<2W_fB^dXmXUHu-dAB0diM_-v$t+Inumoe9EbRTrR(T zQJF&zzo~yD1hgsTE()~dLRE{e7vh$djulqN8g`=+@go?ll@u2g?U)HOi=QSM^v1ir zRUK)KXt<>FRoQWH%_{OED&+Z=^Tu#=otf>UNr?GDF^$V*SuE!PTZ8Y=+`x1faO%F& z`+J#)NQT?d)pFAs!Kl~VuP~9@mt!zz$%B*mD=>e8qfo?EqWS=;O!GjOhP2^S8qfX7 z(z(1hM*t(;WTNi5uQOu#D_b1!+1M9;6dDCm@r}AnWCqeh{TUJJ*#}wy{wRkWwfp|F zW-){WeRgYtkM}GOrAr*&jivq7L=A$5Zi+9Z&TjoLSs)mA`-d&{|9UXR?1*gV_Drp}^ z*R9r5b==(2Eu*>MhLnzFHdF!Wb7&COR70D*VW4HmaW6hyLWxGFM^hc0A`Ve{+S6e{ z3|N!_b0j5q&r2xK&=-(Sb8ss*hcIPSm$!ebXy^9XQ{&g-y+zctAvt`Y3wDdwl>2w# zDQg&Dp+IlsUsC{UL0JpD~zm&7Lk&MR)Y| z4EmM|e*$Agt<|M}V?gNq&$zL;1}e7j&bB8@Ksc6c#skwPDrOa->hNWt5Iyj~I!Lnz?K_cDYZ?{kR{9b#9E)0|s|gmR2iRK0ryWfS@u z@uo=OMg^u;q_$i{mUO8PYjvH!p>W6hc4GzXyfd0mlYE%+pe#Z=woE*wef*l%J8)g) z-n~eQ?tc`1*fUbgve5B*ZY!!zx2JzRvJ`eC=S6Ip3?BJs3_=o@Xbi5C9=O`k7%N8{ z1Uj)49FHSiUOYist70}gWggqIF7L|a_fYK-Mbl+_OS`En5z!n%2nefL(;;zY>x21q zbF%|0eQ7i;v!yk1jz$8!VCOeDQRHoFj!1m7!K{GVJ`qFN59N*(_u2vQSR;R!^sy}4*w3u0DaQ5_$wcyG>Y`>wfiQrWoh;UgAvZ)33759h!`ksHDLLDeW zz+ILjE!9D=ofdFbq3coefZ6%BuAd?AG{L<|yJ&a{Gms)2Qm5!pP5K3|UdKetI;s_& zBIzWvoFkA`>2kjwiKgFGw=REETBG?8#qTF6?j$URl5nCQI5p57+aN2k0dpzNZmDFP z!M4c5PtgFcJUNjAS+WvS$ZBH;MR;eq4(7{sUro+CShzg`csg`hxOlsK`>V%txHn(- zZeZziIOCvO{ye0m1F6ctWXg@p?jUbJe1myxeLQ{bZ;KLKh4Az1J{Nzjum8BHh9Ydzk}}qh*r{;fq)f9osxQq@~S?Kc=cPr)(z3ewfqiSUJZ<=BHQDu#tO}djwR9U6_bNw4;X2o;&7P`Psx*Kz_rPXN6=NA z8_#pr%gCu*;=Qp&cSk^_4ykkaJy@hLgT`GoEC^y?%aFn zEi5)YIWiGLrJ%m4(&j{LDQ+SVVET9s&o>mZ;%vx?AwCW_X;CDtg?VvqCVpped4xok z+Z0(m>-_eT-Wr6Re5Kn1nZeqB7lH`a5MbQq+ZiM-jim@Y$8hoNU5VmhLpChWNJ6Fh z5sO|NJX}dvbs2x09NwG1ATn>DFRfmy=qvDXJrkDY#6@_6Ojk1#sS9sPO4+mLCDVIP z^;!P;^030hC-C)2BBMTIbnqmILQZDl^-{5EWKTotN9NCrrCX7n53CeOsmQf^@(kM+ zGZG64=0+HeF1~a)QQh0tte*&~41fzcqi;>ixs{AXM%aJRm50Sx+6@7>Zr}XkN2iP; z;|v|=aS^cte1gsoX6s%-m$B#b%Ck!L|Bq%@&r5%TUMdVSK) zGE~gFy}W;*@e)=oZp~?ddV-~^H4Q-SuPr|d%|=QqOG0*= zs#&TO@`VcvMvw;*_a@LPHzVp$C_GAovUbE3tv-L87|RU?lpk%34vyB9QJQ}L_L6FK zlh-zpqNLl1kH()d@06&rzv?|c=~uF&U&G(T#Jy`qMH_zMRH*eVh`w}cdXIgs3eDEF zG3gnUKcSw;8F$sxw(b2%vUPD6o8&a6ZivYDQW0bfZq`{`klJYWhC%+H0#Q-uz>qR zJCXe2hWSZ^>(k?VC2H@p7-R;qg{}+sX`>ox=+BnyR+>~Ap}}fuc~8XZRyos)oD$<# z0Ll4|sn7*Z2USA*4>Sw?r-!UH!ODN*>`c^gFMax=$=?WGE>BaCnY?^W`-!8fbQM2# zyQD@BO}g8&gH+J0+f)beT^b#xv2XkI7;{Yeg~1A^E7waUb_Kq?EumTM)bx9{_0x6hiT%Ba6fDH6gB__oN+t4kTp|C?rJSps4yiZM zjsz4M3&gDz>p(6XNb@~MBLT97<&p2GM)Hg7^Zb;J%KNzD z3b-X?+zbE`D}6-?vayTAk+gp-(&cXHnep$khXdENXeqbKtYV3G5MpGVjQC`*(TNimAeIY8i4E%+SK^668$$LCD;eZ^; ziQ2X;45Lj5ziP+Fj7B(GYS&xML4csSQx4tyu*PY&z(B`CtT;+%@I8P2N6=C$oNa}b zfPV9PldnuL63~;3qKu&m;Q4H`(x94??||913&E2jp78R9BGQK76V{ds4fS}rjm}5| z>m0;fobENAzMs{Myu$>lM%A~@{IQ)t*}`C;Kqm9p$n)E@w9CFin7E5NEQ%2~uKBA` z!)4l)eBDMk%sVYG^`U>m!WVaJqAHW)*D>ZZ@UiHf5qcX=thSbzr}0aZ{ufmQ-jAG2 zE0zmT(CAJYXcKMH*g@n+D62?=Bh$yGjFctPZ6 zJ=WOq`MVj>ivH<6aP59$MO3??sbbk3F^u>p9%cI>%(P5UZGd0&E$1jEsUBuGPAHe0 zt`G3+#{-iVtoMJCa6LBc!fyBCd0=)^mVj=S>O4c#NV+ipX|h&C*FnjesCbuPvSWN! z@#FeWaZ+tkIGxK9Tp`Wam93wH{YrcgH@{hfRRM#r^h)4inYvtVW^f7nJZs6q040;3 z#TSC9&Ls+FmdbAJdK%ok4A`4q?1HG!pU-a)^b2T)?dX4QS?6JlDU=OkH~q&-Tm6yC z`h>1BKFrQ#g2s=HDke0Ir=F!c54d6l4t#`L`91C{k#4U9KAS;a4eP#;yw!S`1LdW3 zuw^sXCJcl;cuxPCC)iGXcgB6C?j$4h#qvYCRbKAC?ukA!4Xz7QLWJlG38NOJkC;K< zN;~H&sE&Wt89qg(J`qVy`_ZjFy%H?@rVBEE-S)$#FD`2CK$Ar>5bUu`kYmUXqR8nrDCV)8Mw51n1b8;2v8N}?T{xv z;43s_zV{18YAKk-@UKiG1h$ibR}>(qyDf%kiSK_sIT0*#ncyb3PvFOI_{3*EqJZ8s z_A3q8XN~R=PKR4mbB5)iZ6r^BXH^JWSm;pv-e--wij2GdT3fN__S59sVft3+1Q3qE zJD)PqrtdoB09CeA6L(5UodE5}o{#S+dx8^_UC1bt>wb%-bb3sAk-aRuy@1GAxi-D7 zss4ZISFOA`%PY)Bvb_gBbuyb7t>M93N{iySbiRpj5K<=fn2#+PJd!alS z3f&Lsn=7pKihwUC=R_8HsLy_D8GLJc;k-jbUkp$L)|A6Pnb^1{lUheAyb^ADhY@L6SdDV!MZXfSVlGF34nEs7K-Ckp$V8shtlCyenzkS%#pziJ6MuAx>_ep9)qP z`3697(m-l=30rMK>FoKYG_`;Fw2!#+R_m**`EiiiS1Z3?tbOg$c>(bkv!30rM&9Eq zN}$Kc+Jkt9to<33T4SJ-KRQ673)(q;W3?IF5eb2cVQg)w=IoG@+Z(drfYjnPFNlc>EaOi_Qt6h;BSxs3S* zd)*p+ZH+7EJ(5z)wTP^T1&YZW6>;QGAz5n@`qQTF*oU>_)jB@5wPR@Ih)KR;Gd)i0 z7pnaeWr&4;n_r;?w!LW<{HC{T{#M)gYM^Fs{a z+G33A3w{tz@$jzkT)wFTqBx)t-`bE&c%9x1HPU3lhnRjBX=CQQQJqJTK#E*yMvGFG zw79>%@gTM5;@l%dwSesNFLIqc-oPKSqt>~bXtg4(pf1==IQY; zGNqZZY>&1)sQiDk6u&+wkQcvjY$ucVJXsk$d7_{jl=kVZ*JTfts%Bta=DGRg;IKpT zkz(yEllWwH>93G%xtWkEmYi_OW|DFIw;vJt{Sxi7dXs$Kdw+R-Cd?EQ4b!}Ck`L-G zVU^xn|I*rp-0`J=ncQtdtA0%)J04aXn?<|Y#5P7ij_ZG>hJk@*qwX}j;K#Li%JCzA zoAtwstdv;3qEM-sOXp_R69ogqN-`lar-e?$Rj-kCK=KPfEP&!AgHk(_jm;GnXuTaH} zCZ~-3i4XP!GFtfjBMw?6mTLM;bxZR950~Q`-Iu|N0uur^GM6xD0~8T9FgXe@Ol59o zbZ9alGc__Xm+_$k6a+LiGB%firUEH{b_qCCd-%UnmqaB|ksLI#&t~68h>$&$+ijM^ zjAdrb3`6#6>`G)!DH0)Mix71~5s_;T*+Phz61w^w+VB7WJip&_o@dVay!-opKkxS$ zn7E~l0){}rfkqTELjkFX&;X8@+u9;g00MzlL?BQ+Fqkcg;SPS6^T1AkG&+fYLe}`j z;s_1IG9Z}&mH`=>Q^>$EFLwZm29PQmNL3940ze@U>fa41G!4K2>rEm6=8C{E3K^vH zz>ZL;zBG~xkpboT{s~CqWdNkQx~lw!JAm;3X(T+B447jXM9>4uh{w7EHWWMwWcdCT zLRyQ+U{E#Sa3+(fi1nZ=QfMxJIx_MAlf)nb)*v0Ed4mLCy=CAy)&u<3nj#MjuqBe{ zUlleKX9g2X0|7|jPQrs^I^^O-CV(^m$`06=m;n}4ko?uy?5lx1@NGB%QW5zh+&Ay_ zKqT^pGZv4hcu=uqUlQ2`a3;BffQ6BnBEyFv4`9iJbwjK>odWq|y|E;JcPtJv*eD$f z7-6gcEY#t*{^)obiOQfW(n;>?J;K++Kw~x}6OK?kJU}vo&a+;h0f`3Up}G6Qza6d{ znZhLdeMinDGQoMh4T2XHZbv40dVwYe-)tZe&rg{P$N-cP2!t9M4S=2i=z}N1*Av+K zQo#)+a$OAd5a36pPyuIus0}cHGafi5I6 z&(G+P7vhNFaH6{8wwjz(Tu#A-lN$7$k{qMDhU%mLvwAxWSFD zV!QP{b0?8OOA4KTw0^Sy3P=RvKRRf+@NUrUL5C>0p#q`Z`8}f{8BZasFBwW%1;Em1 zSYI9l#3YomGT?`Vwvhn(Y>*g$E0QS;$OVA93jmxcG@kV%RYgO*Sr>gp>Hys02U1rB z;MkuC(&2xg^;+SCUmy~IgFm2>G5{y~Qi;&z@zVl|=JpGJR0H7dzd$Iu$4`iaqW|KH zR080%Umz5o{tHwA;EZ1&l*sEB2qp6V2~kjFpAGoi9PwT>8g!X#P#qfbcf4_PfuIkF z=jrXD;59F}7GJ3TRD=;>DvY^C#=pL(OOi&5XwfuVb zG%DxIeK!YxA%xrMA^(&p4WZuB>aEVWQF;Hi-P$YDbTcp18`Ij_lV{Lmf{V~xh4oV= zTdzZAue|5`@y@iVqgqlG1xt5{b6khZXB2rix5h+q?Nd`j^KQOSgGs9?(7%W2_~)U? zA^L(|>ukKJ(bRHEeKSgVq@gcYeiLapMda;UKB_5yJMFDb^5kO`Vbe2hKnHB>vEjbe zSm|@@>I)&#IQnzT*eoSsXWNDQ(^*~?5*I>f6|==-#xL^nQKK()*|yd*suyHkPF)^X z+}UTQ&t33fgoY~TjZ*ED=% zbx``H&&XDsV=}^dPVADyQbxwBoA8seMKiY@N}W2ESz7#&KKqPX56nmr60UWf^E9AI z$R=AbwWI&iiY&C=gM6~xc%J$AwSGdf)uD=i#R1t>%3=rK(OiLuy3}h-rOW;AI|K6A zwC2ACDnd`^cfcy{@>QOFmB8jKVbnE6O>QG!{Ad*@uU%JvLY!Tz$lE7;ukzSi_O6HF zbDZL4{N*dxMvAUF6pDns*w??!Gc1vK*7KxmmO#2w$q}!D%@*w$qZg-$dm5Zx^HUIi z@TbdcO=mt?ry>cQUh?5L?~=OK+I5N~F^5c%mYW#2X^X52&Z!qXw+DCQ!!v9K)hBmq zocwtBYGroEj&}Ueo^2Bk%@^L3{mafiLHqD(+>5(Q*eqTBRESrANWSzosf)Dx!Dg32 z*rnc@{|?`(2*ApPkeMO2Har{|?eAoN;Me<&KTCBRmKyC}3!5?`-~`g;lQa4YHGMoA zl0K{)%j7@K1Mo22W~#2pDI6V>DAy7!SPr*Hf-g5v>ql;cIEgimW9k!v=8}Ztnjc=P za+Vs>FjKyDul~(!je(bbDwxm8z0I{J(nhea;rcG}>!2^4v6{ycE?;IDPt9Y02)X{b`AOkwySuyf&IZV`E^bSI;a3fwfOYH_>=yw{R>I!usPu4)5-iU*N2kcJ`cryq zfar1A<>N!>H_xfnI}-8a+;Z;;MY;ASp|T;SsP(-_jKJU4o!g}6Zm@Gc^Y@8esgoUu zzU;oFys2bXtgUezTV-+T{NoWZhbL9j+=&iE+cSTHG11;3j(={6;|gtm%2KUyqp>xw z%WmI+{+xP}qhwqZ)Vrv&V=X7c+dOG_+phha41n_X!c0fKRaxUTffL;p9U-LYkaGl= zy@A+|ErdR%Ub;MWo(oXdsBhjUd|oO}Tfng9%0W|0Ub~kggeASp=0xALGSs`V{f!rk#%5DE#V6m6iQZq(!e6!io>R`NK{64jU2`*OZ!{RTr z0>tiRoAG<^CFm)X5kSzPjhkH&2ivWRPS@{9e;jO*Qk8brw)S9uv8qWNN2+uXyK-y4 zfo5fob?l|#FxA{fnWXbFcH+xnOH?n31ByvBsTRp^`BMXC_y80U0(|Pu$@;yCG zCvMY+j=Z?Z$XM}bjer=#kLYvs%r2176DX2nBQnU9+uB#NaWYcWJok41P8**@oR#0NR+ zn@U;*m@eBOGR83vf7p0WX`x4Fzm{ORZj`Sdb16~`*O{|_Tl$TLo1qi+WzY^r$9c7d zpz4n+2R4t6s$#WnIiRO66m`i2W&I&p7u_vGEKl0P?sJgLcCw(@&}6-GzE+o(Qd%b}14Q z&-`!qq;xBPv)sbvoU&PBKoNs!a%Q!u=Av5S4WC=~aXAi&^|fzjFO^(pu8j{~FBZ7$ zgiDn?`gCsBEx!T*R-am%K^5dZ_EwGcmhaI*wE6M7^m8>BX|;VZLo_cwc{}>r}bAcB7}^#rTZ(JC&Wa zB3@9fFPIN!ecE@w?c-`^lEW%$Mt4p}iQH;3^iSfn;_!o3*AG|1U?axn4sYdmaw|5C zd9mStTm8%wLwJI+HVX$_wG|o9tDX&CLy>dxySahLCyIQr>e*Vyan)zt(57xBwWBqH z`_?oooY~9IyR!-p;==^*NEDJP#s{vKW7dud@@|oHnFPVQDBPTexLj&^9N#tQuS0?q z>rrt<%(b$xflt@9Jnw9I_xb?t-jmRObFHd>-MDTY-V}kJI^EniHKpLr=I1rcSs;XI zDkq)l>E$T$nSWn+T3qO_RhH%(jD(awJuys6jC!bsd4tj%zj;0#w@(x(w-Qj6NbFgZ zFTi9(`~lP+N)?Y0|3hT^;2WEK2F0~uF;Tidd=%}r_*ue3LM8D8?mkaxc5rpCO|D#j z_|6;gPnH8&4v+JedqgOD-JhG48awtE9Z-|HtkGyH+La=EaUl5IQS?9h)Um5|e|wlK z7g_{G)CG?U2JLI)x!atfE@4UJ8ov32p`2ACG4x5GqpfN6l|V4!Qr?laP}_qT^3x;p zRgMF_!~=;h%6J;i^PbbrXyqR_J&uHbBdX4yDL(Ol=yZRu){y){oTJQd>7K&;RIHG# zn@K@Hos|^n@Q~^JL~qn30U6xuZen(D6iPU?^;zQ*aY+&PC zpR!AE!3ANk{9B-`Nn;BmMWjF zW+Wncy2aUct9wzr6akgft~L#f+j<~Jc%VyMX6I-`k{Fijs&FrB+0*$xp_>jlo>tTi zeB8xmaLegRCA}@!=FOi%?9YW_T5VNto~}sfe8TnE*!pTu7$FP`_Q-9N&{kyi{xj#P z6ba6WUM4t?-LZaDhltTcC=%5BZT=yzJ@+wuO&`0N+amOBimNYGBG?9e4ZT(*4E@iq z1!QOcj@$NhlvZY4asp6&`Ar=$zkd-$=+d1@?gANkXhQQuGC(ZuHFC zKC165MN_&F9>n2n0$EPsJDL0aL2XYbcB6DHkIp}SCu$^{eZKp~$#+YbM8~M8CyU!ZcP{FU`-Tn;S9aj>7e!Dyp`%B^Tf#6`kwANeyu=xj^Pu+$gmh(E` z!6{JvekxFTteX-jT==zbyDaOv^pO>}RYkr%flVoI(Zxfwmd8b$DhA&j+_vX-W?o?0 zEu$B27M6F5sDebVTfE%MlvyO8o35TJvJk-`wf+1^rEB@T!kXWnD}R`9MY9DQK#eSh z`LouLGFBCr!TF<#BA!WUZ`a29riNR|=ioOe|J0_Q3G@ce#=|*bHGHDhquVdWYghjm z=@Fv}GG}-_?gm8Z)DmFvkpEe<53?S;4eQg_dnj=~GFD9q*qC4pR0ZDE8DHM80N20& zk)D6TdOX5_o`2bAwY?GSzX^HbDYCe+9J_P64~f4p^!206N-|D58?U(938)*is@SAi zcz$QpUd4z#)w2n12Z&!PeX}U4YTP-!N;{$HvPJ8o3l{Dr%J9)2=-<0vFG{Z(DMzz3 z*0ENS_!%X-8zSSVI4q>GDQn=m{rIEBONvLfw6o7%F+5?t*3IO;gbbmPJ@PrnRh3uv zv3*LE<+fRPEp-7WFQ4Jxrp1=y)3BT~Uj}J(ogo8CK&dE2?eNx`E}4x^M^PYY-kGiC z0RU-jdCK{(!yhUxc4B5?CsSKEe*XVG{9z$xW##7h-w`V@4+}e2+5kKR1_U!ZH|u|A z;5{ML(RI68-DE?OS=~Izwy230D5U?>VLXM9vtW=I94rkhofhckDFY+zDa++~pM8Du z@(I-Z!=;C(?nds8wtWYrs|to`%wSodX+`^;6%82)Spu^&TU%lN6`$wDL_{EGC8*#* zv_XF|j94y0ikc9^XkQ?M^H9$r#Kw;USp%pZwkXWIQB4AYX+-^WKhu|%E=)(#AGegahLBRBg0#F}ny*4EbQTOu(t!Yt}0rhxAPaKRfO z>_N=Li;&i^&pOP)TT`IVS{C}NKR_JtP%fDMMIhj=Y@r~*P{4fSAyE*-ytqRcf|Nl> zxrOB#h%)plt@vY)0#5EAcqF#5iXFx^uViyABFqb zBs3V?@v^~R#UgGxp!PJ--R&k4ZhtVxp{d&K#y>B8U=*?_2ieEEGzo)D zUSn__p&m2{zyJ9VVtQpt``QWkGJo08{Leymx7N0=63ds8FJRFUiP-n44-tj#96_qO z4;SJ)O#hcdCG`9GSS2Wf$PCg~jYd!wNo)|p>iTCMXRvWJF~F5y0tMq}a%ac#iFW?Z zZ~92gtPBnY{?V-llHJYE|5YNEO$X06)B`GZvCo8o^4xn%_}@CXuAbK zW&+Z-DI}c^4fNMM$i9iHgs?K}V6suL(sOK?LFBqvE~Z#lFO;gP@Khgk#geSUzo_n%I6K?;VN_ zaYbb6fLsP?-Utl^12JFg2S7HeyUu$E1gbhFiti9M@D)XUqqetu!`GB|ci8$4{uiu0 z2oM&J*8F$jT<)_9Qr7+QQ%8w;*?>U(b>t~5p38X^%PD+Agb9Olf*g60yhVhP(EM?Y z5_xd}T&+6^6pBae#ABJYHXOhF->>17~w+5D{~bE|fA!K@mYJ3F8{Kw(E6&8m_?5O_0h ziW|`H84Qcz$HoW}VI=((7lZ#SLHQxqV4pFy>Qw{>exdB+{PZ9;JRP$bD=0WnGUEh^ zFP%TEURhJ~7ONEZyNz6pQ#Xt{uNNQc2TN@;ZM@NN<1-N#M6W4J#Dm=snUS(R zJ{d_Gszv!YLj(rXjr>R#_GsBOLpAkq1Wp>WsX`bYZqul;z&1vBnZ$kOG{7e=7<8KV zBB5!kO>7i}mz=LJaS#x>>u|@F`Zoo7)HubueQ}2ZGT7t#Zn-7dlomrbKYDN=EE%65 zcuIZzQsglRz62J6Dk;>z`oT@CUp1{XoroaR+nX`2^ld;KdSRE~_aBAACk2j2F$`oW z|L(^=!A~$okZSANqO%+;{8 zW0~RI+y&>J%?AY8w1W&4&oecKv(Y?B+Z_}7$G9M-^RRxc4LR=C-&9=zyW;hNhOF1y zwL&~5sx@18no4=pR6^>wkStSaF*q90O@lwo!m{U-lBvHj<;Z>?Rhh)b@GCANy`ZEe zBFKKqS0YB-hRVeWLI83-cyS~mA{)D(-5F^0>slfKtLmpAh1p58dktsa>-39q6B<&p|BmxLI^MsW5@7LJ2Zl})7 zXk?-9iFXX~p^*c{j=~tfYM~kBwLx^QqYM&kQ-qSpt+VA39AOx;frXf+C(mY`9I97$ z3u#tOPuosefiiOljX$V5y~$+@t4At;bB|R~VD>3s^krVEwU+ErYn>D0mBaAnApDva z_}Lq+-87o_@c`fw!D6%<>a9>Ny4f~=Z|Tz@?G>C3#CoFUWDIn_IgRKZ zx<2D{vAM!+OQ1bKS_?M0L-g^#;yw*d@L{aS6&^*d2GpNJ?g8 zBZmI**^+of7Av}$CYk}Q zBeQirQwZRq!`=yPl%#oszVeqxpu4~=aq}sWJ-+|wtTrCq#<`9?O}Bbkf5Ayvic$MC z6mStL&n_mRI0yrDtk!iV62e8s&IVBkF83|qso0j?WS{Ht5P^GA=p`T7wm%9;LXbQ> zYUw?|j+S8_J24^hGsFu>)Q%enPobyTt}t zwnh#YVLatY8tdF|TM7Gnp=lWErPmOuj9??X!n)NM?)ku7eMbJ3dFJ7$kkS|4)qIKcpbCvPW zGhoSf>-fzbiJHnz%Ml5IzxK-|l;>CF524!CXX4yi| zAfV^z)!)m@rW*E_DDNB{q!Xt{h2Sj=1%#D>fLHVJ{xQDfS!qDwPcyl1Ba@yjWMOBv z!kU|wgogxzFGD+|gC0b^?D4?IPm=}MB$@!Ml1Z5#H$ivrkeM6F_EYK)#?6g6*8#*dJpXS;mPDjz7V%Kmg!S%7qC zmDyZ#m#Qpxc{u)d`=C(4;sNHQA%@zNE3IZ}H)MCekG$7F8&ZD*ER4;Q-qW)mPcSh+ zW|u`Ue3&U8;o@Xt98z)`H4IO=(Z3d~^7HSd-dz6>1^p2vsUAa_l3=8tURKyC$5M;p z{7Siql4Q*j`Ij z=M?%%9DQm`$$Xj^kJ=wQmE~6dWhzO=y|%vp9j3oCzl3*d=~uDH%hBPpXLnHJK^h5k z&}&l*dZ``mwaj*pyd@;W*w^XvT_9Dkp+jcDLeXjVzk&Eo&unrhj0CW`&#w9dv3ktne3-dtm69sb08(`JMLHHU|YDNjDo`~ldW%YUnUL%iRc_RLZ= zK(-1m8L%gcCdxOn8?D9c&a#R1lU%ktY&aAl=l0-07us$Ca!H=KwXst68gOVG#fteH z6B{ZE^5)Vw5nplf>XdR$w;^n261rkls_XJXi3fvCah<)a{2hh`%zdhA6Fu#fN=%gt zDs3WS@@jDz^8xuu{+Q@RL>vlnl7;0<7tCyzl<5MrE=g(=vz~`L@YHd5C&ncsAEii~ zleK%DW&?!wlu`AzaJ?kQK0?MV^wv0hq$rH2kU^LI@WYRY{XsqOzr1CzLo_ZsEQY3b z(7DVhY~m?^fRWr%;NR%C95fi5Hx-UiHXf+gd(Y|Kd|-gkN5ASPR(YzmhNz3K*)JG^ zpF-gW-*mlvhujm>@29u;)NA3^_praq8;!N+pp33VVx|{U(6jjn6h#bYtv6jl^G|4) zDrra(AraU3PHPf>P8%{M@fb*(O7OQwpXxcra4pzBjr4@Nhj)q3pbb}$Ry!G7zQZyg zxQ#=2mH}0AzvP*0zf<;B&lZdx#Vw*;po^cF%A$DRu&2&U#;mVKW)+3dMn1Fluuv}KA5t4m!GYy$OcO27N&;SUW1;^eC`c{Q{* zad?F7o3_63%&|S>DuHhExrBrZO_eAY8Uw*)tXpl2Ark)Mxjsws+HB(eR;%sgvxt`^;Mx5rdGmr9n|&J7dCjr0=~L-X!{RRQ4l z=Ad`zP@KaL{UC69)!t7R^9wDKw-61RS*iACZ8T7l5)DbB!#-vH2@p~gjo3*T27YY_ z88{0^Pq>OSW0cg{mRGF5rT;pw8#R63-;Io$tNTtC-)IX$CudFm*TD9Z1os z_R^Cj5OdQw1kvSMjDbL-u8!ryETv(zk38}ejCb)OQoscHvRHdkth6U|Rh3@Yn?+s^ z+k0pVran1^j#gO&A+!5yMZ4hQ-KN~lI?LbGh4%DvbvF(cDGe3455D}@(kD` zv6ZY5uH*TDHPO}vc~S(pX`^L~9xW!n{|M8_-IPE2svXuDi(Ng)IeGfP#N#}Ec=cF$ z%pEG6Tm|F0%GU6-FTrR03%m_1!Vak09=6M9d1XAOTzOkk@a2$iW6b7^3*`h1{vLRW zFa0Vf=vJdskNeYerB%Gz@gzPIrKN-cE2cfi=y3g8HEpDaDo&M7Pk-@My~xRRMX~}^ zCY6)X@OA_;4SZT?p(sA&HU6IpNokTTecf(MJ8gP|iI0*{vzc6C7O12tWCm5z6x`mg zdgdQudI|LcMOH(AJJV+MnA98D%YyvQt4(<5Q#Lb?i6X#Phlknvi`F3hqhOw;JV393QMdwcJICJ&lXDZ?ElJpPU%tK$4f| z-6WGTi#H7*oBm!e383puB#ewq$(PXvF>#kt$o~@W;VB>t%833&bMK{uX^Q);_YQ3L zr{{8$A-?lY&itWiC#}Qkz?WrBNK4)m`3gwWI9m49QTTh(lKFG-e-Orfh9bKow zY5bL+jWw2UvUUmEWR9LOPnl%jaPZ|=J|rxYRyUbe)v<)UbpgQu-#|%O{|ydq<&oZ5 zRJZj9#5webzh`=1PyFMcX_>j6Bb3O;^>=u2<4S&3mG`lY20!?pkYOa(?Wq}Zwe9q* zM6e(0)bn=iXok!PR<~d<&lpAp^>U;FgvgrN#1PvE-r?v>60))QlhlbC%xq}u6DRwJ z@+s)aAGM4$mVxy_%OU^LF{@Ft!lIR}h+)I+@x8x1T&2c=f67O2J28r?fNTkD-DUHZ z6%&x&kc1NER4%5QsW=pZg4mhgxfl!Uzb&j5Om(ZYFS3f`=3TBU*@g&y$`qnp=q|6?4#Tu`yzry?_$-f0lacUGuIp$iUW51U`QBTwri=b*{X?tGg+cbIJUkfM#I2P<(NSP^rT7mD~Kt$|gRM+1wwMtX_Bjo#-`F%`A6JmykkrH0kA zJcK4^_1tO%eXZzRV@PiU{n$JO_+tPsT#Q{`X z1Q`-K)Ipk_WR`>4hb^?ogmv{JHN%N1wRf=TS_D7H63K~)g-@|#O`k+duW?W-4^HF1 z>aC%z>elgPPVd=I6l7SE(rz^4TL~UFPBSb6v$W@nj~*@m35A8GT&PahnACzEP0uf4Z z-%g(L#SUzt6JMNoC1M-cZ@GTO(qoGSQUJB^jCuEATbAAvjF@d6KYUbA|90RtY{5mz z=54ifKdA5f{XM8eH4yAlyseZ9zvq9i*sivQeHqxgNZkkRcsft>ILNhII#PF);eb;m zVX3pTQ1i0z|21fx-5Q0Ju^qJ2jM>qpKhx4+$vZ1D{*U-_P3}rx=;3UXG1Q*1VgS&; z>;6(2@>D4s94D+?N^B|QFZF4`$DYczlBl9&P}Y3P(Jx~RT-^dadhP_GqpnUw2V^lL zOsR!PSv1bQzkJ-q*X2Ew2vS;A#(Y_>xA9zCxm;N>%?4Pdqdd5!7z zlnNF4btJmg6L4g6zxk2$$s$vANPg@)(zJf`8sH1D`!>ofKA!0P-Wtdf&-u8%W5UaV zdLd~T(Lm|qv$fFO=t*HIgvdTZ{u{xzWOrCzN!b|O8jH%UC70pUKuh-LN$MPHvGW$**I55I5Bh368nm@+GF>PK!Y50KV>mV z2Wi~=hD$*i_3no{bB1K>v+;Y0EHNdMz;64t?JJ~df>cDMZT1mue;5I*qX^~sQ zynQ`nY=DhOJ;N^l?*-IKAMlXn%Un~45>+@V(M{4c-z6<8Gf0as*^^Aov+#okt?tSY zR3yrcac9weZ)nvjc^N_?e^y*KeXttYg~9{p228f=RZC05V15fPjSKjI;1)|n(5C&F z+Ii|C87j`YqbLrcM9h$=mF?cEU`m0vP{v6N-a%+Re(s>|@+v2uSG71gM5LSf)b=oy zlHMh|vl~(WBkg2wd^WK?kRdUAj_GGZiKkWJ=5_vx*X^jJ55n!*fgBuM%#fKj*TgkU zURRlan2KAkn%Lw`PAGs|$u@k^jI5oen^zU^5m~_Hm*U`W;P3B=k(C8kDTFov2(BaXvfKsZAK2;Cb-t%PH7%?%S5S8qD1!Qv!hssxZ^O-N?mJa zCo;CDRJEc({&Syj_Q#3PR0kqIh?UXEg08&EIR?Mc-5)2xtVKXF35JO~;7CXVckne( zO6i5v6Wx8OEu&DL>rntvcP0l#95rCK_Hm6Qu!6$N6dZ%oQx4<{9m^+xz;~dd(OhjU&_e%b&F9c1EsxBwgkGMe1#EL`J;>(DIgj*Nu;brV-H1qY z`wtAKqRt;Sn75*K^QS7NdlA34h>FZHtqm{It-$ykU1}qyFV0v#SZ8ytQkaBs8 zB9M#F%Suy+?j(MO^OUHpn}g8~fsd*|dG9*fzMc=CGD71%U!b>WkI1QO3#}oI*(-xx z=088yxY1N$YhaNianyDq=7PqKuFo~|kj|~07f}09HU41LvH4HRe67(W@OlrKPX=$X zBre#3I%)#{#1uBWA^fvmH%N3FRX0DCgW6K}L~tR(AiYZks%+5qW5I?qZ*+c&@NzS5 z+xiyL^Vq>!bZFO>Uu$MYuKQF(i*-d|JgBj#+P}}+gy(lbHPP{PUiY>Q>|iAIyX6tl zT9rFq{n6u?7|J=|-W%nGGwa1ayqxGu99CA}11y1bLom#4uZ_veu?aBYbuR&431*Tr zQYE28p_Sr`6d(q*{%b+9|329!d1}9Ka_Dat{INw2YIGkC2?=s;Z=h$^ohW>xozswJ zsXQ&?`}DIu=kwsI^TNjogelTEIeVq_pewLkH-Lj5D|i^ipqYl`dsk#Es!F{P7nC7~4X5UP>eP;R?OF);s&?$1D6P<=zcz!5DX)eCDqy7oX z`_la!LFN|hJ)U_C>c=g(yf9C{rV@ZYPLOB`C(6jVOtr9?dl7ZIhx@i=ZT-N{GT{*sP7y4nswzgjE; zf)-!vH&fJ(AZu7~HE)nj2%^sS%_8a=VB@u171tTKDrrO;v@IB{FuD@XvB?TcmEOyw z5S%()MxXv>g_8ERFW7;&Lu>zvQBCShPk`-biHp^+Y{z5t+2KlTqG|XJ5iP5WL$E0&7>z+f`vOi*pJ^V0^Z&&c8`T$`#7T!I)H7wd_ z9rd`dluLL({A2!fft(tnufn&3aw7?3}ZC3%HWS0 z-qA8!GhS#koZn&laP9}JlsQcAS{L;cV6LgjQ*M(9VsF8gxY|4?Vn%E*fyi%`2Z3)p z20e^<+QdV1DH38z79LjnpPrI?C>=G=ATf_#6DQ80(MBBh*q(Iu8piAIEnFW;F=}+m z?l@h$H(U2q{5+%MMfvH|zjNgq$9`Hq$|k_<`5tXsBS^@g!0!tfh8aQjCRQGo3>Zj8 zs~^b(FnG6*e69b{S?pOsfVdjfb1JmaS{8u17FhfurBp0wmcaL>4S+U~+{eVm@I5y; zd=h2UbZeFudy(5J^V3*!e=S&TJj9rCT!~62I?I(Lcy0EV729VIlHm1EQPzH#BWW!8 zSrxft@ve`i8o>|L(&P9rK5=Y&*P>(n#&w`f(^Qkm#fq+t#FJ~=r!!K2HUD@+s{VPl z2AUZ#B?cNN_Ue9lw^9ZX$E4p@fpI}NM})B}=kW^LTYh@SxE&mPN~df;f50oP{uT`7 z&!{fr|C7|6%nA9G=KMYi4ONIqs6PGF<^ zX{#Li@;6rvQ(vCS3NP66@g>&^Zh+xm6?qd;W7VvK*N=z z_MvjRwnm1>jSAljlOlSwEnhZt5*WVrwTM9lkd;^6<3KOHo_3~6^HVf!Alk zQMpCbC|$uPkN~>*b#W}R*G*s~Mo1&3_Lz1Ug%0#Ny5@W60Su~B6l)H+4tJXr1}a|F zf7nAl_T)xvg_#r0qND4jYCP<}O~SXukcPl3&fWpF&1XLp8fF^HQje)e1q7#Si{F`~c%mu2k zU$F~vx#SyEDQy#cui5lGi(1$fHxLvKGrMYl^Uj|_b)xT>j5VmA$Px^j1yMQ0aaq>} zYovZw_T4=^o-7uEkClIJzGLO4)hCgx@{v!<9O*QJoUAdEI^mB`+m9(z+$sy1n#n!3? zfb84*cF&lmXNdwql*T~U2@h3@hLEr1AS4fiU_CQCE0mqg#Z+o|oN7W>OO1+D?W{iZ`VhgDNl0D$ZQ^5WsaYws`tHU~+B4J)?C!W$~?ZtqKqxCd(kp%zjogl3c z$8%B6kZ5x?-c2S&S7JVD?fZjW^I0znz)5Fljb6V(Jf>Y1M*A1HV#=Ku?MWBR+W_L! z?ALD)gyrgVUhSN=^dBNG#&giB(vJO=)V$qTe33HxPx{*&S7`73rmC)=I`}dtp__vq zVCtQoZ{CxnPXE9|MR)hBzUgvf2TRpU66Fb}aSa?5vr)#T(UV=CgYLUU?Fkt#py^8E zwinIyaR$#8zA$q)TL$To)Dr#LkQtO97El47Ar><^Jsawt zn*^mZ_hDq~|$XgBwh8Fe&-+3`=Y4EaF5m?^t`1Do?A@<>Ty0bM7<+ zb?^@O0^0@L<;pK_Wft^c$@K}}&zdx|#TO`lm-L_}Rf`kBs!8+05wudy~V*^T^TJTOKAltN6ClDA}1qCWPDe$(rrMhwHxm@J$##;MqNok+J+=Bzl9 zTL`rnV4s&BHq%U@1)EPOfFSiaZaP=@ui~|v^v;C&G0PiU{jH{TJEdbyG}|e43XxjN z@0ZqdK6k7Wq1~f|kpkX6Q*6<8eJzdg?`n@N{3HJeOQB8_E0H}gk)`{mT#N?Dn&D@; z0BCFEo}yeH>YhXuSxKkCZ3I=sEEbB1*L<3h@=3Oriqsyi7!$|tuPkQ0XnN-x0K{~A zzU+VKOV0nFzWl!iB6enuw0?L9Ob8Bkmj5vjwL&W6uQxLO=z@SnvnTd)UfI36ngJ0r zcJ@po9^imQ8ra&}f<Y3eIJN^Wgd^t3x{I-nF&kW&dT`V{xQC<1e@{0*p zGZIpw!jOqe^J{0|jZI9fOifJ03JO)Bc{V`4$RrCEAb5F$F8ckxNF#Z`6tYQJzkjm= zV3ht@ed664K>-=O#M#*zK+D%Mf&ymn z`zO}ca^o`Arl)_74=qQH&&>Z7AOPObq@w*@#INpJY0x0iXI-gEp;JZRSp!KW` zpZixr%Y6hv{Qyo@ddAw?dRCx-u(4bqTTi-)HrNP6Hj@J+!!`+^vFq^zYR z#`~rQ^QUe~0VI)>7UY|B&=<|@_!O%7^~KNuMB|GA>75f-JfmFT^zenfLy)l*Rr1bD zR0w{MWqhkI^Ot`kQNM?yaoraPJj&|ko+s)Nq;Jij?j}K44UasG z@#2K;`)1N~L=APtWuCTnFusbuVYIkHd$G8HVy~^Pfo9{|gD@Hr2F?Nip9&}F2Ni6p zP%5i2Jl{V!uR}ERUg)Ls^Z423s|nXF@awU}8ahLVIPh^c^1R8pF0Kp>@ycu*fL2SiV)d#ij)G%_5(F7V4G~uC`zvBrPn2-{7 z2%Hl8T0DZfIB|;{-TtF68#%uSRKV;ltv-GGR=GPsC}U$O390GhSxom2AiNqxG3W75 zK35>X(=+-#W>CI?6!Q`VhUJh=T!SC(ficH?Vt)K=2c0qn02Jgvop+#pkD01A2V z(ZBohAO6ijoCKio6_b9`^BueY?EP161h)ol`HgBUAtYPyoh4LR;fq)k#)X~b4J5Q1 z1#<>CxcCCbzH;vX^!SOXPmydCD@zZ~c(G3^%2B7Nn#;V+fIPvbmp)>0@*AZmfD9UI z%;%j%c%k(fE40}3B?4JR_cI`rIOIJ74A%C63Q`f~Z+#nB@YitQnDAF{{EY4wylCdj zA0rgir98iO;?s?H!X+UeX=M8^VNIAnMg_!L#7)4~zv)fhggQjpALF%P-;K78j6dS; z`+TQC$I)C$iMs%IAvIMYdQNl_k(=0UqmWkRtu3mj*gcp88~Rr) zC`l)@tl%s7brOl;okk2!vU7&8ObG(sAGVbe2zH9fHr_V@?9pmD>y9Ei=|ksvV{a6y zzomlZr^*?-_dgDHYjAErTVXrJQfViFfiP$>9_Zuc8m)<*(z@qS>yxK{hy@%Y&*JEw z&4~3+>W{!*I+ganXI&xCVP<}iOW&`SU8Of^t+1^cQ5lYz?{618<4ZOj`<~V|4R20+ z*Ctms@wlU@qMf0|_ZynTDXREJT~SQ#&H7?sh}Xc*044=y!MY%xhuP7Yc!q4iaZ(eD z>~!Cq026ag@Lnw_^|tB;gg}-U&wzf3qw?B!&g@1yPQf1vzi6~FZVBy}$gUV6G?fg_ixBfjf5j|i`V#h?bdB;}Ql1^wxdnJE*{o6{TU=7I zT8+%TK$23)lW`l6cu4vOp7x^wce6H7F%dhVIjY8&_afX;CH&z?~pKda7*n<~z zZ=xT9Goh-Y6^hg8d$T2qI*$g#1&AF(?O4;+wAutS6KaiggTWk73N?~^#N~O3H$KWvos}{lnGS-;b?Re-2*$$&TirG^yHbr$A%xho$uD$%d7jYY1 zHXL^I`l)=}LAw}=luok%Qf|C7HQ!w!RKmshZ%NT@+`+zl={&0B|Nx^`oh4l+U zkB`E?h{i-Gi#uj58)4M7@54ZOh0czAEZ=<2buq;$G#EE#pW7gyzhxeBTc~?R?cp(5 z&i0rC&ZXCi*FJ;oJ6|rJp#pgQfDzo$j!>2P@$k_PgB64!23D?y13LiwO0y0Ih0$eEn5i4qaAgvtJ7KKu?JL5;ar}H<3L?K!pVBCwimSHwI6YqNsu0>?syun z-RiQ_2dV2`+I78Ymhq!FXjl@6NP8{kNs^lYCvon!#HKN#9q43tJeSvrp(!Ph$X?E0 z>k}gOKR>Evn`*YhvFFiPVcAYCFn80({cL@(Pi_9No_zn~J%v%oLAa_GO z3DWe|xz!%GLpg`pXkLoKs9rY8f>Bm54APc4sJ95_GCPOaeXln(pnAgj zEnpM3*(D*R)P0gq!T*hB`V9AT!ihB$>F zK`EO@6kPvU4s^WEUxIx}B6Vf&xR&Bk--JjyPToVj0UX2 zqBxE(`No6!p@P6G>UTxhAWaXY5z;%|^W#PsHAT{jLN z!(CWRXhC;7bW7(%Iq^0b6VAdVJC(j4*g99Br5YEmfNT)=eYW-P;!e1Gtv&WMyor=* zwT7`VVj?#C{X*~;Q%aqC^X|q@KAir|djj9Y^Nh-AA!(Zw$;JrjVxL;)igdpJ!3_w? z{xN=qrr%IWa+o7?G8M>3gf-jacsHY;2 zY+Y+|#L>x8+f;@u+`JmW(wG(lk)M+WblYBt6PM8F!pKSaTV!H>^M=~9#1T_pPEsNV zU!Z8}yPrbk-7w=ibcGmsi!uJOi6u~k@;&rQ>msRSXqTZiqtZK?LjCS)cuNb4Y+MVc z!(*G9QO~DD=zE=&hB@}Mp&G9SdEH<|uDFn&ZwfQ(Nb~jUdH1;RuIp>dRA}Bhm%PG= zx@3bi-2!wsnvv0kheGYH{Vei{>o7}qwOiO#`=b9bBOA7aV^}V0BRtu@0gBe zP&|dI$RM*M{yIa#mjn_0NB2wNk@d$7?J2^;IDG2tWIzi+Fj1y5icy^6jQ52sMp&k#qB846bIwSG~BU`3IEno*u{tLSE(O%XIn z?zOj1&UbE?6k6-b8`t~qRE>Y@4zF8Z0goaPj)VwRA!ewaOTIs71Qqa-ztqB*{X0Wa z+FBP7r~^wVi`q=Z%`v)Z_f0=4)QqSeb0^^Y7Lhz-m$s$E>6e8|Og=3HAvzO(8u8y_ z?-2Yk-uZ`xmmI_ra#atlSp+6C&g=GeU&>EQ4!Po zWw%(_<+LKiAz9o5*gl|SN~VHd^BK)n9P=;dh+6*d0<1jtBF(@rM!O}AJ2F9MjmiS= zeX9t$CGzKRtyg#M*6YiciE`vk$o3UGMv*;$z;rvozK5yIvc>Km5tJkr7PqJ7;>we;c$lS|)wSWVq4GFb3)u#8EddnRW zk9&=bv!SDBq{{0fYM{}>vp>y`C7se^#kOp9onG($l?r;v%EH2)WMYtd<}$>)kf0fD zj0pUtq;1j&1Eu^;j%Jx;Ve?xHYSf90_m&PTq4n~K+lV?16zQ8n4-vL{<$iT%lr$3i2AM*s4>iddn$1AVnnD|4puT^)<;D!2UWreDi3#c2UBK3x&;N znUaDTG@m1~^w#OwDCE^>#^lw@!V=L!j9$ljykkr}SMT*5Ci#W`dA8XXtzu{P?z(VX zbbo4QKbVZ6zr}zfxxIPqKd&=^(aRkgmA@P6m7$h1ofm6Pfh`NCk!@CoZzMZ5IAzkO z8|xtd+Qs$^K(KAT_w!B2q$NYiH79Tu+a*P-ZB83wn}-PtttqP(nbpm9wX&RcKf(6T zRI)C4#gi!C$>N#r8=~=td~o_lMpDvb-jaBwK$RG;5TQU#9>~q}op^Q}zCTDu^(Q45 zJb<#fST%@v$}>?=Fb4VfxQ@xbzl?OLid!hi%H#F}WlG=HtnJYRFq+4g-7YDt(;f)7 zAsrKW!w)NLYA6zjuytpC+2-@aE{O!9nMG^vPWM#N$g@Dvs8WQ*VNz4y^v+QpffV6&gJ&-E`I`gm z;!DN>f8F{Yq7a}%W;f`OsiVT`o9)19ALgHmltjwpRs|6ZY{$G00`3D0eWf42J%rz>V+bDP+Ot=fgU?Q}}Z8L0}hO=Fb?6-SHv14q)Ds7tw z4k}ldsaI?SUnJEu-K>($lprI#NVQ+AjmMsVAjCN-<8{zkL4(UPu%a(~biAy;LHq*E zepv4>pV1EYX7Tm1!uSH{g0bhxv-D+DU6-$-uRN|_8Y&VBr&_;s$)+OVcVa(eXkyf; z{VE3c+96_xgu~w(*W?tS^w9E3$Qb?=TfJ`yJyxNuSV%SpE*U z>{&Y6eXrw41VP!Jmw2w|iO}+DwPhg=vC1K%#RbaBOc{Bt$LU%(qr3dZWA#gKseCTx zl$~Owuw;&j;r}S?tfK0Onr$6C5D4z>65KXUa0u@1?iw5#cejm&;O_43?jGDBNN_*= zNABx=t68;1S3ULUF>3WU6Df5n!4aZAhcWSNMu>q!)pO6=oc!1$cc(EoQb;}{oB*7e z5%Ts`rE=##%uAAOmxb%EE-c}T9)3|;+J~7n1yLEDK}VVU{rvuRDm%-X<$ckcvq1sL zbxYr;atA};J$&Rrp49d#gY1K77jv13YptH_KS!D%|I7?h^IV}Ev-T<+8j)zd(x}Uk zx7q`s!wK&Wg@cUXf90#;5D%fzm3I9N$lKZ!r2R26F1J#g4NEIhf#>225Bb8|bf#L$ zXEvl^$Mu|I8C}y-WNwA+P)P@}xRi-*MTn^h;bW?T-y*x@Fj6a4F#rWE>`?o0W|RxY ziVz(NCd|Ipg~GBD6dOxjr_9M!gT zKL8`1puyI@&N?ZI)uo2;q@6_~hgyQjm>q<68j9{LQCj)eEG;PYVyb4wbi8tj&f5ylZE@f~zym6Q zyWh->`5P)|J-uBsw@MUGhsq}+3R61vNtPdM#3ueaDMC{JEYYkFr7-%qLbp+N>3;9r z*R;k=(dPjdLRpRnD;}X`U~p;ZDllT6zIC%`)lhl3yT~tnsgSj@D(iMA1i?*%oMT2ON8M#qq@pQ!Ra>BP|p-!SutTJut{*}A;}LTT`%fy23T`-F`|e^5Kb%Qv|* ziJvE9YVFKyl{D59^c9x(2^8jm4F-8pV{r7{5mmY6jvebpl?M`(^rpb zH$|8%*ZN~wP|{S1%Tkc~2k5*!65Na74;M?phIO{yVbb!!(NwpT(~HWxAko zlk{%`wW>6bDGXb(ztpva#N0h8qy10 zsta#6Y+uKA0)Ctc27c7`epYx^y{O0Vz1ls&{3H*7noVN(iT{SOJ#bXjiYqyr&Ra0#JyfjyWF6h( z-LTw8);7HOle4cI_c4t)CTQ3l{#$pYtN%0!i;{FJM%zCNT{zbPxZmQMGtHV4V)Kx( z3ES5rIx+qMm~dsOS|*Xk7022-cx@F9D+N;ymr{}l!zgJmlL4e?ED^|wi#we%ys9s> z%nMz@;l~hJyzP;z%n0j+P#PUAHB8E)C1!dj_wPFD@1xwPZFpyoIg6jPa1yF$TG zqZ=t^+t2l7KCq@6KMS_T3ud;*JIbB|cz$rKz!e#f#}Z_qX--NJQJGU$Ehgl0N#Fst z1vU+ozO5=%cmPEL8542H!`5a8}bXEg65_nDEiudWv9c%!QM z!0>xnQp`5mCS?Ny!_eANv2G|_LA1#agd;0hwLMjM&LiW}FuX0*R))~}Z`nGt9Jo}w zv=Jpy0zTxA6oN#V5q(m(4-*SrMPhDS5d=i0UUoMRhO!b5Y67f~OJnx)KP$M; zbwBM4IRavygzZ7`ZeK|PweyY5L_At$XNOlM6WqzPV>uQ<0odDyPH;cDSdJ_)JN2@8 z%&ROf6HsI=aI_m9@k3fG8lxP`rG_qz51Sj(b29p^2h~RQ<7y$#J}kc-r0|7RJWOIg zYec>gN|6Th19_Rcy07WPUdT%$~^F?X&YPufIKm;2SvRON@r zbr4Fk+&Ui3a~NsXtTMT;1MxxE_}kW00|8I;wg3jS$O2!eUGW4bC9$bx93dd>g34NI z324tK!0Kij?EBW;s-`^!xz3(`o0;^;-WQq7-@ey*>7m}+XkU3iy~ryyTNSxR4qUV6 z`I=TY1kT7Scr`~iB&0I2T>l{!>-=kMEezpR8Ssrl%P zK66owA^&_QoxSJ}>xLJVi83#S?wwXjJYdN|s+5vCb*HHS&D@~(`G-NSva(-|41|T% zQOC*e)9*=ZQZv?EEIjhIfr@SbT(sO8kB{e!DrPMQ8|u*)Yx@hEcFLcF7M zlk9py&>0qP=7sme%k#;f$nDLuUj96-T^1SyyNqGlLJhFL2wPuZPZRHU4pCjLJpgX$ zJy-Y$SXdvZHW4Ig==RqJqMh$0W`YtyGWi?f?h(k}jm@0m(BpALDF1B#h#BDf_6>*o zh=oWQbqG6+CW|8H@hjt2fQSm6XF~c${`z+`;>a$FIc3Y0eMd9}jKd!ddu0kTbpOKl z!wIdOr7lM}goIaTE$81%?_{!mMTrBh=i^>W{tbJx;$k`Lb7ui)cF0rYtzsnF;phd9 zn02~F8CgSF9$&GfGV(UetLOB#KIy1+W?+vXy|6S2viupXY22tw`ZQVmjpbpbZs9V2 zHKk)cB>VboW%e(l?m*cwN6C-z3>lUI_18eeKDvRRkxQr(9)|9RY>e8{{qzCg?_$4A)T37Em(t< zFEM3J@4fj8pLrvJhRZ;F&9z25Ws&ynF(yhID`OB?t$NRf>HV2MlqU&Ky_;w^+(iwJ z1;A*)L@hV@x&45)u~UZbZp{WTT`K>+i5YzP2FH9cC?gwk{%!Ct88pb$wEvU3 zow?Vl_m<1OP{S$snE^y`6#^>7s$OM+5HsV|-FYeFr=h*q!V2GxX=;BNcdJWQ7At;q z`Ag=w_NZ+RRj)bdkbBW?yQ^Xs*Jdd{#87WujXyXY1d(-t<&?#>aHTq?FdMBYqdJr0 zcJa$oyHOEqe1kj}&^-ZMUWFuJf4y6O{NKLUkU379?u1L+JCo168Sd8xj_hlWZjGy5 zx6bq{c(fD!*2=Nc-13FpQjzaZ@Q-{8DfG$@v9Z3%+xz_Gcyuxo<5DvuezG5)ld^b>LxKJT%GJ5#5Xrv zf>mMC&eFyY_BKX*4|ZnLVWsa>R;#E;HzZf4|2_SlU$xNK%WF~dH8@p&O$?g%$r zb^OQfHu9>qeU*>z2>4JknIDB!HI#3!6+d@=^*(x=#_T*vrL4>yS0Z{*5PVBn8m9Dx z|8SYqp3nejJ*Y=Ph#7WbC#?wy!A4`A8(pljge`3D!#nTRABPl}wc*awK+b{nj-13Q zUldC7EG1F-hZ}j`dn}7P0|xtm-d$b4|MmD-gpMvY3|oM19pf|<3}^?U&(kn@9}F{p zbnD}m)m(9`h3c|;ojf8@Cq=(DWW)*OKnqgW&_e-er`25YXFF{yGdVb{Cc(>KZw=`J zPQ2zS9HJ1SAA*D8ri2=mg+U&T(asCjxMVt+X=UO!x(Nu}d5a>RoEAI^Ynp%M<_?f* zy`ol#moejh?~V?VIBnKx%eedm;17>KpLy1dMg7u?VF?AfFzz#bB^1-g zGZn2aZdiyOoi;1+Dfz(XO#=%{en~%at(3!?m%v4%oM#bWd~#g)RdHC`m3AU}9ec2l zd?P{jisw%C6^_q{$wIZDqi4a!d;5u0Dq85I_JB{7U-v#OK~c*hW`qoN`sUEa>`)D$ zvs7Y63N`heSmZV7vWFXoQl(r2b{ORBK-)jYL;59RMKY{vHt6`}tkv)p!tY7f$Sa_6 zkTLP*0ai6AJ0Zknk8@pC%Id{!X-oJd51MCugqRm(I%_5+12g`6E7MRM`XQywbg+ih0$n~%gW zlSVMm5tA*ve7UH)dN`pr62|JBzD=Q32#8;V2Jzh<5heZPpk)2xCD15>zM zW*c`E`5J7-&Ux1T`a#GSK-ikzuFxJS`gzcdVn8r5d6lE{wtQx>e_ny7=zJQ`!$T3q z@F=MD&>9IkfZpRMgHA#0G+b#=4_k2qCH2(A?lTvWSH*9W^&<)qEv1(1oDi!af?jIb zFKa~FCDPnHDyzC4YE*-l*UWxEj|qt_{4V_p$T-FEZlBo1AMZAq=HMwKCD&$mCwQ^K zxfc*(EVO&|##J5u^puf_$>IjezA7c+Wuv3JPI5vB7yGomgebIx+9Rf<2p=z3TG8e{ zN6z!nK10e|k~^SQF&-FJk5*4{FK%Hx`ggO;3jdK!8YYZIsR zBhY5W-$GHRiP+04Q?HvB1d|R;p;E8t9hBcK9*a#mNAK}?pLthd#oHZ~n=9VC09Kfi zBnSl#ZE;pBALuxvCo_Anf2~n3@mcE9KH0JE+n)o8=^wuQ^)J(nM{9q>hE6vyd~LmkIHA7plx} z>3Sj&MW|cN-(H5E1r&f1`sG*?(ri(Gr)nM_gNDZ;_aKS`4bne)t&l0Gvr}=@Soyu8 zey{G8%=Yr$q0gpsDJ8R2gnTb&2R|*@hoY)qmX;Qj+u?MU8x?>m)Qdh5`KHKqf!#Ge zQHLf1BskIoIbx}RFW&+rzU|87d&@?Se34u>@~z;Cs3Pu%%ndb*sXGs^2jf}*A?d{b zNIhO7xKBWI=Q6Ls590iIjel`nD9i;hK2?$T3+bIKr48k@3ws1;LBHcHEt?zOwVTFF zlSNh)548V8T@(c3APThaPn>Cy@8T)n5x37C?m7g9ZtrV;Y473>{S(ivF&N*_qj+p^ zg{z9t7O3qpnLYDNE&d9=l}?X7gu_(9;vQ$axKK%Eg;6k8VO?&G<+L*QJG5V=Wf(lO zXl!8rspu(v6U##0ebr?)EBc-#k5H5CX7Kr&@0P!loha4F_0$*G_%BREhfo62*z+ zxa%#n6AC>Zi?%&z)~BINpUC{h8iG<30YO=$Hn7!%)$v7piNGp$c(+YUANEiUMR`aa zmZ;~7=y9+5G3KfTG#CB7f{e>Den~019udR3KeP~7eQ9Y0`7gaz>*2S*f&wN^km-h7 z8?0W&dOUYNl{;@^QRmoW6D}m~ltb_jDnt#|>#GGXp$VqmbsjN$_MeMUz{Z)r1g)*T#R3J8*V5Vt0Tg-HFjp8w%#~@rW|oD!U!~3@*SGa zZ6)=39pY#$9F9Ap&OcM>RAdiwKCJJFbo$LyS)poKE$o~|@jHx{`?czeOK8)Od%rA~ zbc(^Bze!nu!c zaCQ}{;tL$7C3mM;-_eLlxHFsrQy|!Emq{>;da2Z(dY%a;@56ML^vI``D-HhvxP4xy z{_YQJQuF4GOjB1k_0zjmTV8LLIsv5^C#HR0do=_X_c>g%9j>uLZ8#tTSnG7NB3%oQqL};l;9WPYUgeg!LSn$l3^t1>B%ZKz z=r^+r5cZ@fQySv@#k+)p+EAWxDbGGu$=LTjVw0DqZ8Ig#Kq9q#`H9wBgw{E1m#dS- zD0>dyn?O!Uuqnlcv0@6rD-X$Ri;BR|?x(`f`K^Y@zM|q74b|oRk)q zIgO=<7BN2>Ph@g*LF`QL=mREpRfGuV$jb$uf;enOGzZ(ftXna|#l&g=%%m<%GRQK4 z>u|rs>4YWstJ5ormKv84xAl;9c^pTLI-$l9JQ#+~Q=*C?0m~H%xq}&oce^!`CCt8c+?~vvzh@G zRkp2xVXzOkMYNLz2Y4h)o<(8H`})cs8xlb5D%+dDH{_@GbQF_VU4YIM}0ngCxz z7;OU{un>6BtQG%cffZ}g5law1O7lQl+@>Odv3tQ4mwFaRzXwGlx8%QRGpE|<+AEU) zw;e^>GfDaxv-}JQz<|*!jtxg|VJ>^l70--+G_0unLBWlKaPqv_No^Mfq)#evexXmf zXzJJfljC&y!F7FVpUfKNLwydQ`+OG6Ew&PKyF^o5e0j+FdZH}jQ++NYepY$2Q6T*# zHuHPxMv(Zqau-Qtx(+eYTP%cY>~siR+u&K%v+11VuiW1NeGI(crZg-rfnb&?;{lWI zaataHhPO(3m=CxndRjLa*FN_bwO4kvQyYgm-qZw*eeYm-RM|t`bDwQMHX2feKeyaK zK8F9X*qO$bPm|Lycaq@GP~?lMeV4-hEJ`+W7=>r-5*~+shv%tbN-=9+m~B>5r2R2= z_c=AjN%l&>B;EneU?3*tT;p07Qf?&NbOGmdi}N+Kd5Tz|$`~!m&G<7x6TgP}kE4>w zMqh7YLGPOrE&!LMzd2MqPJl7{?xFK2*P>AWrvx>!X$VH6-`DjMUDbo;PokJ@bo1a~ zVp<}c+Srweezx(kN?g1RZ_;Qk^(Q~<_HS07>Pm8w6VE6oFzFr4#&mUSW0u$zD;bphWWU&#-jj`tNF z@^eJwNxy%owDs$J#)F|*i#&Q38#0vb8vi9GJ)wB4dJV9{2qfsf^yAq`~ zW78P0u%%Ec^e6*I|6ah=(*R#mMT!-9g@&XP0BtO|9&4f$uXKUS;mL2pqQb>G{q;Rj zX2W|g^|Y!CfhW9G(lJ^F%saPlDkz-Ki<=%ZGQJfv*~s0WK z6rw30tWMFL6ttCf+!LBH9ESKEyXhous9P2XL!OS(M*lv(UPDc0#Zh|*?r|?hmbM4l ziqKQ!hFW}GoGHuHe4+~x-VvRbB>$lUs-8kt6aO-}$ZcihtQ1%0DB9%MiKVZVYAZN! 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 delta 255553 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 z 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%0AXL>PEqDqc$_Np&KXScWa&& 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�SLe|u6StedJbKp~hV zN6HVY4&c9lAoZQ@3N>EwH4cTshLVm5RLMyWJC)JS8s}MU}qlt_@{1tGMuv1 zi70jMmSZ5U?^?gUuFkYcu?O8HHQALRGxsN%qSLa_H%zg+uQ(TV?Tst$dXMem(E`R0WMJ*U zV+WBKB=lw>hhDyn5`I2R^*QN2|^~wA~w#JRs z?iF0Hg7}z_UYqn>p@0$d*K=Nr{a1$-kJ=-@R>EEzavru>zr8ZxUvO98e_kXwlV3;c zpN>n?ZuWw$fA7R{s+ne{>!?4LI@Fafy>fRX47FSS?EF0PGZ(R$=`M1}nc(}Xp@xZ# z;00>I{uIhq;Niti6xjrcFjGm_{Bz#GJ`<5;cERsF-@n2gf_~2*BjlFiLGo%aQ{n-_ zPHBlZFwA=hsa>5Igt+{F|B`BgeF!qYGiB9}5e_h?RjeuN(z9I&Nj?cfd7JSw5 zTfDROBb)*i_6rBm$4Y^lsy%xEQ!_T9oigCeC)qG2bRhgwNGF+ABCQY;&PoFSHthoc z%T;+Bs`3VfHW?{XY9^0~81Qpsd{@v-eOKBS3#WjYNkxCFDRoikza_2AnZg2X{HO{X zeAx}|f4K=VS^o=;sRG*H^W3GQ2`^cn@T4;fAD5fwQ-LI39o5n)SZh(m=lG|%oDypT zkO(8BL-b#^&EGrdJa&Bzk+^(q*@<{5_>D!eaD8mP>`@d34b25onI)WB@3 z!JphrNF8=%=vQ8O<n9Ye0GX^kz{iMZafQ9nDd} z8QlSRkdp+FFYX@w{&wZo-$MCf%>fwZ=?ftHwu&BDS<3kKIUV~)hJw&N(78jJxFI26=qFl^$alGe~j5q zsmuxmPi5x!MapN#0#XkKXrJzvsy8is5;Fq6sfzr>Hu%`e$Zhh{jqSgwsWhZj=b$1` zvO`9%#<#lFG{R|0h2Zjv&9S6jTwxp!dy8wBhgbpdzZn^T>$EW~W@By@FCRnJBs;QZ z&0&6tSJ3%R(=jZ_(knIBzZ$yOe+rTxa}ptcVkf}*`GEq5;RgDytkE38uJhNQW$!Qe zO~<&;3Mqs;_Ben@N6nvh8zs+jJx5$M5>}O9OQ{l60x<%oQduf{#=D=bXp%J=3;hzq zhk7IDrWp9Zrri8bgPH!y8c^4!V2VfM-f01g6o(W?CM-D|02eGLEEGBNe~sf|Vk4XH zkTkFhhU&T*(jH7!VG2Ga8lAZD=VP$;hbkaUCJjiRbN~f_QS-kYA*3(aJq8%{P3xlU zofnbwe`HJ-BtdbW6xFz^HGSKEzob5;A;XkLs_U)u_?)b{t*6NcZ@JtoOg^1 z`WOzRrv96OSYtFqdtA5GK#IJJ?=PA!rzQaTs*6J$2H`AYBdb|?XG-k}aTGK+rXeQK9 zZus`1h=`ubjOFO0f2rbX28;EDGq-Z7+{B@BpXjiOgc&1n|4qd&r7 zkWn^GWuyg#Y99d!1o!G5bV9%?nc16=D%1(v@kesWvvcewe>9cGqR(pi04s)6n^3|H z+l3cFbxxG@M|zCiSxvsM6_@nqGBCu}tdHHzJ@LQyW4kMqfZo$GN;g&3o*mjz-_T89 z&@^ls>IzD&U&}vNS=B@xaqH>CL(rd%#kZ9co_1X^2OOKY5e>5o+sa-6en*GkaoL z9EjJ1Suun9MzhWybH>Uh89q%+E?HMz$zE1YWeWO=f z?A)EKYIhD6+rv9hGf%AhtzX8WUl~cEgaBJe*33X7S*3TP7&@q^ZNQu;Ynd3W|FmK3 zN*b%#e~!7FBCjW2OdtLAwn!;qdIgUw`8x?nk<}+ph3j(@q)_&mAIi_84&#@r)TSFL z^~xgvG&W6a3@~GtO%$zX8r&BDsy{KgZ-;s&AJHnW$G~YDX?lb4mvrvHW zEOy;b8og~4>tlp|nAPD$gk5j-1>qx=3hL++H7u5dZ323U=zLg*`-S4Dd!0A)dYysK ze{9!cY8QV3#Xmx7Gw^A1+byY`dhr(u{&U)(s>E9=5WORy2j3^4_8p)rAypU)zoi}6 zPgRNjNNkjil!F11<>)M&KYgowgV}tcK~I$1V>1m=nya*{te;__SpzLN2< z(?uj1Pp=VDVfs`RWI^I9V}>L~OQfyVe>m}K1;wOvl#+IUvSZS1tP5;yft0I#1G`lR zGCm!9_;*F@f4lV2 zze|}7CJ%onR*z)B_Q=^@2%JHJtn!wypX`q#;xQe!J(@803w!dFS6F_IkSf{3Jy=k1fQI#4t;XUY`xJ z2jS29G4A!T-ZFruadH$yE>V}qe{r;So~E3=W^V5jqodspg%e!|w!yLH7VCWsc);hP zPu;b}vN#EbP>g}y8GMlh@dJH}oxgEKcXFuo00=?)KeWy{tc1cZhaI=tkCFiYja#M~ zs+*$j#uG9PXR{KJZ!o9UGJ}%sscT-XYffe1Nvptf9Do;IaQ2IFo3uV5~N1$(WvFnX5McZE4+MHy_>gB7R)Y29;Ci0zm9byj@rIIG_nv#AlL4-Qbj zazSEmtluF5bAOT_ogM!BgJ1n9-~=GB?zahBwr*gHs7k?#+hye6O^m)?1&H0)?N+&4 zQHhZ_hp?dl9^XvtJPjn$e~HO8pH~H|uy^1w-3N9r)%Q4EW3J&W0-5qOdr4*`oWtSl zg;!Bw{e_+z(K$77`=8S4m2^#bYt~hV>(E7S(j;}u*iC94cXS$eywkI@HRfOTF_~jX zpL$co=DuMxNM9hbnV)QEr*F8Wd>|bo3gxRis{z4Pc_GxBuaKd#+0_@+1-EaSy&$aJ7EpUcu^b zxHJ~t&PGkYM8X^ze7K;Q;uYca^7M)FxcG=e)GuOb4rEl8k|AOi9}j|iLz&g6^aWHl ziXU9E5C3fZ3tQ0s|f(&xutiE>g)FINEake~72|3q}H{dXVaGdN5na z6@LfI6o<>cvIX!WVyMadlr#5aSFZfU(KjUAE1Fb7WBZ1o>X@79&N&FTzd0-mJj$jF z!1!!c`%6ha#IpT>6w7>ItLzAa6A5Z3Ky8KD00;oj5&V;g`@jK{CO}P|9qV;$kytly zy;_42jH3!we>5l}#rKa9aa{s_Np6`+NqVzkkb_>{Gyv>$+nL3tvK9K^*~_rP$BS2M zsCYz?t~n8ts0zBd%mtDExSZUlpS^ucDK~f!#RcDxJ8FA`Q_X5GBsZv()yPjiD{oe`eOQyyxU*1K;>sNA@QrlpB;A>og_&o6 z=^#6Lf7=D2kI^?KhQ<_?Vs^ef*Ck@F%b|lXXRM2NFm8nUaHGtLa+Br4MImLruPR`$ zan%C`9ktCbbpnG#5x}u#!x(D|dtFE75yp8V2?(LT0i3AIcsD%cD5 zUn0Tn^VO&hj@NR``wV8Ea;P5Gf4n7UVWWo&e_JB zivX`3<4XM$_9+fyEuN*mRFFxxOHoBCiVsK|l*8bx7e)M7VUJ#?%mkzLScWIlQC{w! ze|}DxM+rYI%Ii-@XX&yV0Lsq>bRkB!92V!E)n&9G3U~#ct1w~9|7TV~mUP{&CaHzc zMZBywTS_n2uCP1(Bt<%~7_6y+>K%jwUhoGs{{pQ@3NbjUVf_F<(%#Z)@%+!mEYox- z+x^ziiT=XXsIBt_PwL+2A6eGG|KsYNe|m&rm_W&~ZQHhO+qP}nwr$(CZQGuC#yoqH z-DKbIC-g-peNoj_QWJu+p*X(S(NeYKEr>W7*_X5h&KY3iZCW>DSl*p<7lJceMAfA&lc zNQ^v>fKHWel%D$=MoqP(lmbG9bHxA|*%?>1sB}mSxUOmmL`kd@4|8`b03sEy5DW4! zKviS%>6PR=D?NW0#KL&Oo!j>J+beGh1Ts`X8%h(I(m8G*0S_J1{(r5ip1KS3k7?5f z_JkR4YlVC;G8KJ)=kP&N!D?x&e^rw#_nhwsv->*2>CU+c)1BskWQ>fx=^)VYk4JS1 zS7gSW$32yok;MH1ipj@mbJpddt&TdiT#^XJoa#;P37Kd4#SUME-&!51QIWcs6@2!A z**)H)W<`6lG)`G`u+Id9J(q|JKdzVE-pr39utpJC11;vkd&;%!We8-De@Vf`�BZ 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 O@O{lqFICug_));%LHT8>Hc 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 Result<(Self::ReducedStatement, Self::ProverOutputs), ProverError>; - - fn verify<'a>(&self, ts: &mut VerifierState<'a>, - stmt: &Self::Statement, inputs: Self::VerifierInputs) - -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError>; + fn reduce_statement(&self, &Stmt, &ReductionInputs) -> Reduced; + fn prove_inner(...) -> (ReductionInputs, ProofString, ReducedWitness); + fn verify_inner(...) -> (ReductionInputs, VerifierOutputs); + // default `prove` / `verify` chain *_inner -> reduce_statement } \end{lstlisting} -33/33 tests green. \texttt{Statement}/\texttt{ReducedStatement} shared; oracle types role-split. +Implementor writes the three; defaults wire them. \texttt{Statement} / \texttt{ReducedStatement} shared; oracle types role-split; reduction is single-source-of-truth. 33/33 tests green. \end{frame} % -------------------------------------------------------------------- \begin{frame}{Per-phase mapping} \scriptsize \begin{center} -\begin{tabular}{lp{1.4cm}p{1.2cm}p{1.4cm}p{1.6cm}p{1.5cm}} +\begin{tabular}{lp{1.2cm}p{1.0cm}p{1.0cm}p{1.4cm}p{0.9cm}p{1.0cm}} \toprule -& \textbf{Stmt} & \textbf{Wit} & \textbf{Pr.\,In} & \textbf{Reduced} & \textbf{Pr.\,Out} \\ +& \textbf{Stmt} & \textbf{Wit} & \textbf{Pr.\,In} & \textbf{Reduced} & \textbf{Proof} & \textbf{Red.\,Wit} \\ \midrule -\texttt{Pesat} & $(l_1, \log m)$ & wits & --- & $(\mu, \tau)$ & cw + tree \\ -\texttt{TwinC.} & acc + $\mu$/$\tau$ & acc-w + ins & cw + acc-cw & $\gamma, \zeta_0, \beta_\tau,$ defer & $\mathsf{O}[f] + z$ \\ -\texttt{Ood} & $(s, \log n)$ & --- & $\mathsf{O}[f]$ & samples + ans & --- \\ -\texttt{Batching} & $\zeta$\,+\,$s,t,\log n$ & --- & $\mathsf{O}[f]$ & $\alpha$ & $\mu$ \\ -\texttt{Proximity} & queries\,+\,$l_2,t$ & --- & trees + cw & $()$ & paths + ans \\ +\texttt{Pesat} & $(l_1, \log m)$ & wits & --- & $(\mu, \tau)$ & --- & cw + tree \\ +\texttt{TwinC.} & acc + $\mu$/$\tau$ & acc-w + ins & cw + acc-cw & $\gamma, \zeta_0, \beta_\tau,$ defer & --- & $\mathsf{O}[f] + z$ \\ +\texttt{Ood} & $(s, \log n)$ & --- & $\mathsf{O}[f]$ & samples + ans & --- & --- \\ +\texttt{Batching} & $\zeta$\,+\,$s,t,\log n$ & --- & $\mathsf{O}[f]$ & $\alpha$ & --- & $\mu$ \\ +\texttt{Proximity} & queries\,+\,$l_2,t$ & --- & trees + cw & $()$ & paths + ans & --- \\ \bottomrule \end{tabular} \end{center} @@ -133,101 +133,34 @@ \textbf{Asymmetries are now structural:} \begin{itemize}\scriptsize \item \texttt{Pesat} \,---\, empty \texttt{ProverInputs} (it's the source). - \item \texttt{Proximity} \,---\, empty \texttt{Reduced} (it's a check). + \item \texttt{Proximity} \,---\, empty \texttt{Reduced} and \texttt{Red.\,Wit} (it's a check; pure \texttt{ProofString}). \item \texttt{Ood} / \texttt{Batching} \,---\, empty \texttt{Witness}. + \item \texttt{ProofString} is non-empty only for \texttt{Proximity}; every other phase's prover output is reduced-witness-shaped. \end{itemize} \end{frame} % -------------------------------------------------------------------- \begin{frame}{Single-protocol view: a candid review} +\small \textbf{What's good.} \begin{itemize}\small - \item Paper-aligned types \,---\, the \texttt{IOR} trait and the paper's IOR composition rule line up directly. - \item Real verifier symmetry \,---\, \texttt{TwinConstraint::verify} / \texttt{Batching::verify} are first-class trait impls. - \item Honest oracle role split \,---\, prover sees data, verifier sees commitments. - \item Empty-type asymmetries are visible (\texttt{Reduced = ()} for checks). - \item 33/33 tests green throughout. + \item Paper-aligned types \,---\, the \texttt{IOR} trait and paper composition rule line up directly. + \item Verifier symmetry \,---\, \texttt{*::verify} are first-class trait impls. + \item Honest oracle role split \,---\, prover sees data, verifier sees \emph{handles} (see oracle slide). + \item Empty-type asymmetries visible (\texttt{ProofString = ()} for reductions; \texttt{Reduced = ()} for checks). + \item Statement reduction is single-source-of-truth \,---\, no prover/verifier drift. \end{itemize} \vspace{0.4em} \textbf{What's not.} \begin{itemize}\small - \item Seven associated types per phase \,---\, real cognitive load. - \item Lifetimes \& \texttt{PhantomData} proliferate; structs carry \texttt{<'a, F, \dots>} for anchoring. - \item No useful composition combinators \,---\, see next slide. + \item Nine associated types per phase \,---\, real cognitive load. + \item Lifetimes \& \texttt{PhantomData} proliferate; structs carry \texttt{<'a, F, \dots>}. \item No type-level ordering enforcement; transcript order is implicit. - \item Orchestrator got longer, not shorter \,---\, $\sim$10 lines per phase invocation. + \item Orchestrator got longer, not shorter \,---\, $\sim$10 lines per phase, plus oracle-handle construction. \end{itemize} \end{frame} -% -------------------------------------------------------------------- -\begin{frame}[fragile]{Composition \,---\, what the trait can't do} -\begin{lstlisting} -pub struct Sequence(A, B); -impl IOR for Sequence -where - B::Statement: From, - B::ProverInputs: From, - // ... and the witness has to come from somewhere ... -{ /* run A, derive B's inputs, run B */ } -\end{lstlisting} - -\textbf{Why no useful generics fall out:} -\begin{itemize}\small - \item \texttt{From} impls are protocol-specific \,---\, written per \texttt{(A, B)} pair, no leverage. - \item ``Between-phases'' glue is itself protocol logic (WARP between TC and OOD: compute $\eta, \nu_0$, build new Merkle tree, write transcript). Doesn't fit either phase's signature. - \item Witnesses don't thread cleanly across the boundary. -\end{itemize} - -\textbf{What unlocks it:} a canonical \texttt{Claim} algebra of shared shapes \,---\, next two slides. -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}[fragile]{Claim \,---\, the shared vocabulary} -Lift ``what's being claimed'' from per-protocol bags into a small shared enum. - -\begin{lstlisting} -pub enum Claim { - Sumcheck(SumcheckClaim), // p sums to v on {0,1}^n - Eval(EvalClaim), // f_hat(zeta) = y - Proximity(ProximityClaim), // codeword delta-close to code - ZeroCheck(ZeroCheckClaim), // p == 0 on {0,1}^n - Batched(Box>), -} -\end{lstlisting} - -Operations on these claims become \emph{protocol-agnostic}: -\begin{lstlisting} -pub fn reduce_sumcheck( - claim: SumcheckClaim, - prover: &mut impl SumcheckProver, - transcript: &mut T, -) -> EvalClaim { ... } -\end{lstlisting} - -STIR uses it. WHIR uses it. WARP uses it. The \emph{transformation} is the abstraction \,---\, not the protocol. -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}[fragile]{Protocol becomes a script over claim transformations} -\begin{lstlisting} -fn warp_round(...) -> Claim { - let c = Claim::ZeroCheck(...); // R1CS satisfaction - let c = c.bundle_with(...); // -> SumcheckClaim - let c = reduce_sumcheck(c, ...); // -> EvalClaim at gamma - let oods = ood_sample(...); // s EvalClaims - let qs = shift_query_claims(...); // t more - let c = batch_eval_claims(vec![...]); // -> 1 EvalClaim - let c = reduce_sumcheck(c, ...); // -> EvalClaim at alpha - Claim::Eval(c) -} -\end{lstlisting} - -Each line is a typed claim transformation. Protocol identity is the \emph{script}; the transformations are reusable. - -\textbf{Limits + cost.} Only the public claim flow gets typified \,---\, witnesses, transcript threading, setup, and proof shapes stay per-protocol. Canonical shapes only emerge from 3+ implementations, and soundness arguments must thread through the algebra. Multi-protocol research thrust, not a tactical project. -\end{frame} - % -------------------------------------------------------------------- \begin{frame}{Reframing: a family of protocols} The single-protocol view above was honest \,---\, but the actual goal isn't ``a clean WARP.'' @@ -279,23 +212,20 @@ % -------------------------------------------------------------------- \begin{frame}[fragile]{What WARP migrated onto: \texttt{ark-vc} / \texttt{ark-mt}} -The migration replaced \texttt{ark-crypto-primitives::merkle\_tree} with a multi-vector commitment scheme. Two structural changes: +\small +Replaced \texttt{ark-crypto-primitives::merkle\_tree} with a multi-vector commitment scheme. Two structural changes: \vspace{0.3em} -\textbf{1. Per-codeword $\to$ one tree.} PESAT commits $l_1$ fresh codewords. Leaves are the column-tuples $(c_0[i], \dots, c_{l_1-1}[i])$, hashed as \texttt{Vec}. One root authenticates all codewords; opening returns all $m$ values per index in one call. +\textbf{1. Per-codeword $\to$ one tree.} PESAT commits $l_1$ fresh codewords; leaves are column-tuples $(c_0[i], \dots, c_{l_1-1}[i])$, hashed as \texttt{Vec}. One root authenticates all codewords. \vspace{0.3em} -\textbf{2. Per-query $\to$ one proof.} Old: $t$ queries meant $t$ separate \texttt{Path}, each carrying its own $\log n$ siblings. New: a single \texttt{OpeningProof} covering all $t$ positions, with shared upper-layer siblings sent once (path-pruned per book §20: union of copaths minus union of paths). +\textbf{2. Per-query $\to$ one proof.} Was $t$ separate \texttt{Path}, each carrying $\log n$ siblings. Now a single path-pruned \texttt{OpeningProof} for all $t$ positions (union of copaths minus union of paths, book §20). -\vspace{0.4em} -\textbf{Concretely}, $n = 2^{15}$, $t = 100$: -\begin{itemize}\small - \item Old: $100 \times 15 = 1500$ sibling hashes. Many duplicated near the root. - \item New: $\approx 100 \times \log(n/t) \approx 730$ hashes. Savings grow with $t$. -\end{itemize} +\vspace{0.3em} +At $n{=}2^{15}$, $t{=}100$: $1500 \to \approx 730$ sibling hashes. Savings grow with $t$. \vspace{0.3em} -\textbf{In WARP types:} \texttt{MerkleTree} $\to$ \texttt{MultiVectorCommitted}; \texttt{Path} $\to$ \texttt{OpeningProof>}; \texttt{H: MerkleHasher>} replaces \texttt{MT: Config} across all phases. Same soundness, less data on the wire, fewer hash invocations on both sides. +\textbf{Type diff:} \texttt{MerkleTree} $\to$ \texttt{MultiVectorCommitted}; \texttt{Path} $\to$ \texttt{OpeningProof>}; \texttt{H: MerkleHasher>} replaces \texttt{MT: Config} across all phases. \end{frame} % -------------------------------------------------------------------- @@ -320,55 +250,62 @@ \end{frame} % -------------------------------------------------------------------- -\begin{frame}{IOR trait revisions \,---\, signature corrections (1 of 2)} -PR \#22 review surfaced four corrections. The first two are signature tweaks that document contracts already true in code: +\begin{frame}[fragile]{\texttt{reduce\_statement} \,---\, eliminating prover/verifier drift (1 of 2)} +\small +\textbf{Problem.}\; +Prover and verifier each compute \texttt{ReducedStatement} via parallel-but-different code; they produce the same value by sumcheck-fold semantics, nothing structural keeps them in sync. Concrete site: TwinConstraint's $\zeta_0$ / $\beta_\tau$ \,---\, prover pulls from sumcheck CC's internal \texttt{tablewise()}, verifier recomputes via \texttt{scale\_and\_sum}. Patch one, forget the other, ship divergence. -\vspace{0.5em} -\textbf{1. \texttt{Witness} / \texttt{ProverInputs} $\to$ \texttt{\&}.}\quad -Today: by-value. Empirically read-only across WARP / WHIR / STIR / FRI / sumcheck — no protocol in this family consumes its witness at the IOR level. Switching to \texttt{\&} documents the contract; the consume-the-witness escape hatch we kept open isn't used and isn't likely to be. +\vspace{0.3em} +\textbf{Change.} +\begin{lstlisting} +fn reduce_statement(&self, &Stmt, &ReductionInputs) -> ReducedStatement; +fn prove_inner(...) -> (ReductionInputs, ProofString, ReducedWitness); +fn verify_inner(...) -> (ReductionInputs, VerifierOutputs); +// default `prove`/`verify` chain *_inner -> reduce_statement. +\end{lstlisting} +Both sides land at \texttt{reduce\_statement} with the same \texttt{ReductionInputs}; the math runs in one place. -\vspace{0.6em} -\textbf{2. \texttt{ProverOutputs} $\to$ \texttt{ProofString} + \texttt{ReducedWitness}.}\quad -Every nonempty \texttt{ProverOutputs} we have is unambiguously one role: PESAT / TwinConstraint / Batching are pure \texttt{ReducedWitness}; Proximity is pure \texttt{ProofString}. WHIR's final-round reveal forces the split structurally — the leftover folded polynomial is a reduced witness *sent to the verifier in the clear*, so the role boundary can't be ``private vs. public.'' +\vspace{0.3em} +\textbf{Outcome.} Drift is structurally impossible: prover pulls only $f$, $z$ from CC; verifier stops parallel \texttt{scale\_and\_sum}. Cost: 1 associated type + 2 methods. 33/33 tests green. \end{frame} % -------------------------------------------------------------------- -\begin{frame}[fragile]{IOR trait revisions \,---\, structural additions (2 of 2)} -\textbf{3. Verifier-side oracles $\to$ handle traits.}\quad -Today's \texttt{ProximityVerifierInputs \{ rt\_0, l2\_roots, auth\_0, auth\_j, shift\_query\_answers \}} bakes the BCS instantiation into the IOR-level type. Slide 4 says ``by handle''; the code says ``by precomputed table.'' -\begin{lstlisting} -trait IndexedOracle { fn query(&self, i: usize) -> Option; } -trait EvalOracle { fn eval(&self, x: &[F]) -> Option; } -\end{lstlisting} -Two impls per trait: trusted (prover, backed by \texttt{\&[F]}) and BCS-checked (verifier, opening validated inside \texttt{query}). Phases become BCS-agnostic; iBCS / ARG composition slots in by providing a different impl. +\begin{frame}[fragile]{\texttt{IndexedOracle} \,---\, BCS-agnostic verifier-side oracles (2 of 2)} +\small +\textbf{Problem.}\; +\texttt{ProximityVerifierInputs} carried \texttt{\{ rt\_0, l2\_roots, auth\_0, auth\_j, shift\_query\_answers \}} \,---\, a fully materialised BCS instantiation glued onto the IOR-level type. Slide 4 promises ``by handle''; the code delivered ``by precomputed table plus auth artifacts.'' Phase verifier knew about Merkle paths. -\vspace{0.4em} -\textbf{4. \texttt{reduce\_statement} + inner-method split.}\quad -Today the prover and verifier each compute \texttt{ReducedStatement} via parallel-but-different code (TwinConstraint: prover pulls $\zeta_0/\beta_\tau$ from sumcheck CC state; verifier recomputes via \texttt{scale\_and\_sum}). One side gets patched, the other doesn't, drift ships. +\vspace{0.3em} +\textbf{Change.} \begin{lstlisting} -fn reduce_statement(&self, stmt: &Stmt, in: &ReductionInputs) -> Reduced; -fn prove_inner(...) -> (ReductionInputs, ProofString, ReducedWitness); -fn verify_inner(...) -> (ReductionInputs, VerifierOutputs); -// default `prove`/`verify` chain *_inner -> reduce_statement automatically +trait IndexedOracle { fn query(&self, i: usize) -> Option; ... } +struct MerkleIndexedOracle<'a, F, H> { /* root, proof, idx+vals, OnceCell */ } +impl IndexedOracle> for MerkleIndexedOracle<...> { ... } \end{lstlisting} -Implementor writes the three; trait wires them so drift is structurally impossible. +\texttt{ProximityVerifierInputs} is now generic over \texttt{O: IndexedOracle>} and holds \texttt{\&O} / \texttt{\&[O]} (static dispatch, no trait objects). BCS handle construction moves to the orchestrator. + +\vspace{0.3em} +\textbf{Outcome.} Phase code is BCS-agnostic \,---\, two \texttt{validate()} calls in \texttt{verify\_inner}. iBCS / ARG composition slots in via a different impl. Lazy validation \texttt{OnceCell}-memoised, so perf matches the prior eager check. + +\vspace{0.3em} +\textbf{Caveat.} Prover-side \texttt{Oracle} (OOD / Batching) is still concrete; sibling \texttt{EvalOracle} for point queries is a follow-up. \end{frame} % -------------------------------------------------------------------- \begin{frame}{Where this leaves us \,---\, the next move} -\textbf{Paper section (accumulator as typed state).} Describe the IOR trait as a paper-aligned realisation \emph{of WARP's protocol structure}, framed as a \emph{template other protocols can follow}, not a contract they must. - -\vspace{0.4em} -\textbf{Paper section (structured sumcheck primitive).} The fused $\alpha/\beta/\tau$-fold inside TwinConstraint is the natural extraction \,---\, lives in \texttt{effsc} as a new \texttt{RoundPolyEvaluator}-shaped impl, citable from outside Warp. +\small +\textbf{Already landed.}\; +\texttt{ark-vc}/\texttt{ark-mt} integration ($\sim$16\% smaller proof, prover matches with \texttt{parallel}). Four PR-\#22 follow-ups: \texttt{\&}-borrow, \texttt{ProofString}/\texttt{ReducedWitness} split, \texttt{IndexedOracle} handle, \texttt{reduce\_statement} split. Tests + benches green. -\vspace{0.4em} -\textbf{\texttt{ark-vc} integration landed.} WARP migrated from \texttt{ark-crypto-primitives::merkle\_tree} onto \texttt{ark-vc}/\texttt{ark-mt}. Multi-vector openings collapse PESAT's $l_1$ paths to one proof per query batch. Tests + benches green. - -\vspace{0.4em} -\textbf{Implement a second protocol.} STIR-lite or WHIR-lite, against the same primitives + IOR-shaped pattern. The friction it surfaces is the most reliable guide for the next iteration. - -\vspace{0.4em} -\textbf{Write the ``how to design a Warp-style protocol'' guide.} Walk through phase decomposition, primitive choices, IOR shape. Narrative document in \texttt{docs/}. The actual practitioner-facing artifact. +\vspace{0.5em} +\textbf{Next.} +\begin{itemize}\small + \item Paper section \,---\, accumulator as typed state: IOR trait as paper-aligned \emph{template} for WARP-family protocols. + \item Paper section \,---\, structured sumcheck primitive: TwinConstraint's fused $\alpha/\beta/\tau$-fold extracted into \texttt{effsc} as a citable \texttt{RoundPolyEvaluator} impl. + \item Sibling \texttt{EvalOracle} \,---\, point-query analogue of \texttt{IndexedOracle} so prover-side OOD / Batching is BCS-agnostic too. + \item Second protocol \,---\, STIR-lite or WHIR-lite against the same primitives. Friction it surfaces is the next iteration's guide. + \item Practitioner narrative \,---\, ``how to design a Warp-style protocol,'' \texttt{docs/}. +\end{itemize} \end{frame} \end{document} diff --git a/src/lib.rs b/src/lib.rs index 9abaa7b..e2c1448 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,7 @@ use error::{DeciderError, ProverError, VerifierError}; use protocol::phases::{ batching::{Batching, BatchingProverInputs, BatchingStatement, BatchingVerifierInputs}, ood::{Ood, OodProverInputs, OodStatement}, - oracle_handle::{IndexedOracle, MerkleIndexedOracle}, + oracle_handle::MerkleIndexedOracle, pesat::{Pesat, PesatStatement, PesatWitness}, proximity::{Proximity, ProximityProverInputs, ProximityStatement, ProximityVerifierInputs}, twin_constraint::{ @@ -445,11 +445,6 @@ where ) }) .collect(); - let acc_refs: Vec<&dyn IndexedOracle>> = acc_handles - .iter() - .map(|h| h as &dyn IndexedOracle>) - .collect(); - let proximity_phase = Proximity:: { hasher: &self.params.hasher, _phantom: PhantomData, @@ -464,8 +459,8 @@ where }, &ProximityVerifierInputs { fresh: &fresh_handle, - acc: &acc_refs, - _h: PhantomData, + acc: &acc_handles, + _f: PhantomData, }, )?; diff --git a/src/protocol/phases/proximity.rs b/src/protocol/phases/proximity.rs index 53fcbdb..4e552e8 100644 --- a/src/protocol/phases/proximity.rs +++ b/src/protocol/phases/proximity.rs @@ -26,6 +26,7 @@ use std::marker::PhantomData; use crate::count_ops; use crate::crypto::merkle::{warp_scheme, WarpCommitted, WarpProof}; use crate::error::{ProverError, VerifierError}; +use crate::protocol::phases::oracle_handle::IndexedOracle; use crate::protocol::phases::IOR; use crate::protocol::query::QueryIndices; use crate::BoolResult; @@ -51,18 +52,23 @@ where /// Verifier-side inputs for Proximity. /// /// The phase verifier no longer sees roots / opening proofs / answer -/// tables directly; it sees a list of [`IndexedOracle`] handles and -/// triggers their (lazy, memoized) BCS validation. Phases stay -/// BCS-agnostic at the type level — the orchestrator constructs the -/// concrete [`oracle_handle::MerkleIndexedOracle`] handles. -pub struct ProximityVerifierInputs<'a, F: Field, H: MerkleHasher>> { +/// tables directly; it sees [`IndexedOracle`] handles and triggers +/// their (lazy, memoized) BCS validation. Phases stay BCS-agnostic at +/// the type level — the orchestrator picks the concrete handle type +/// (today: [`oracle_handle::MerkleIndexedOracle`]). +/// +/// Generic over `O: IndexedOracle>` so dispatch is static; no +/// trait objects. +pub struct ProximityVerifierInputs<'a, F, O> +where + F: Field, + O: IndexedOracle>, +{ /// Handle for the fresh PESAT multi-vector commitment (m = l1). - pub fresh: &'a dyn crate::protocol::phases::oracle_handle::IndexedOracle>, + pub fresh: &'a O, /// One handle per accumulated commitment (each with m = 1). - pub acc: &'a [&'a dyn crate::protocol::phases::oracle_handle::IndexedOracle>], - /// Phantom marker so this struct still mentions H (handles are - /// trait-objects so don't carry H in their type). - pub _h: std::marker::PhantomData, + pub acc: &'a [O], + pub _f: PhantomData, } pub struct ProximityProofString @@ -98,7 +104,11 @@ where type Statement = ProximityStatement; type Witness = (); type ProverInputs = ProximityProverInputs<'a, F, H>; - type VerifierInputs = ProximityVerifierInputs<'a, F, H>; + type VerifierInputs = ProximityVerifierInputs< + 'a, + F, + crate::protocol::phases::oracle_handle::MerkleIndexedOracle<'a, F, H>, + >; type ReductionInputs = (); type ReducedStatement = (); type ProofString = ProximityProofString; From 48ee03f2567dbf2571a2918f3c6d694d4e13fd85 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 17:14:40 +0200 Subject: [PATCH 29/68] refactor: tighten prove() input handling, perf, and key types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three small, independently-justified improvements landing together to keep the orchestrator surface clean: - perf: hoist eq_poly to compute_hypercube_eq_evals at lib.rs:192-193 (in prove) and lib.rs:548-549 (in decide). O(M·log M) -> O(M). - errors: convert prove() debug_assert!/debug_assert_eq! to ProverError variants so release builds reject malformed inputs instead of silently producing bad transcripts. Adds InsufficientInstances, InstanceWitnessLengthMismatch, AccumulatorShapeMismatch, ConfigParameterInvalid, InstanceLengthMismatch. Drops the tautological l1+l2==l check. New negative test prove_rejects_mismatched_instance_witness_lengths. - refactor: replace tuple ProverKey/VerifierKey with named structs WARPProverKey

    and WARPVerifierKey. Updates index(), prove(), verify(), and all callsites in tests/, benches/, examples/. --- benches/warp_rs.rs | 3 +- examples/profile_phases.rs | 4 +- src/error.rs | 10 +++++ src/lib.rs | 69 +++++++++++++++++++++--------- src/types.rs | 17 ++++++++ tests/integration_warp.rs | 14 +++--- tests/profile_json.rs | 4 +- tests/verifier_negative.rs | 88 +++++++++++++++++++++++++++++++++++--- 8 files changed, 172 insertions(+), 37 deletions(-) diff --git a/benches/warp_rs.rs b/benches/warp_rs.rs index a4e1a55..987d944 100644 --- a/benches/warp_rs.rs +++ b/benches/warp_rs.rs @@ -9,6 +9,7 @@ use utils::domainsep::init_prover_state; use utils::hash_chain::{get_hashchain_instance_witness_pairs, get_hashchain_r1cs}; use warp::config::WARPConfig; use warp::traits::AccumulationScheme; +use warp::types::WARPProverKey; use warp::WARP; mod utils; @@ -56,7 +57,7 @@ pub fn bench_rs_warp_fields(c: &mut Criterion) { |(mut prover_state, _x_w)| { let _ = hash_chain_warp .prove( - (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, &mut prover_state, instances_witnesses.1.clone(), instances_witnesses.0.clone(), diff --git a/examples/profile_phases.rs b/examples/profile_phases.rs index 3f4291b..596dfbf 100644 --- a/examples/profile_phases.rs +++ b/examples/profile_phases.rs @@ -53,7 +53,7 @@ mod inner { BundledPESAT, Relation, ToPolySystem, }; use warp::traits::AccumulationScheme; - use warp::types::{AccumulatorInstance, AccumulatorWitness}; + use warp::types::{AccumulatorInstance, AccumulatorWitness, WARPProverKey}; use warp::utils::fields::Goldilocks; use warp::utils::poseidon; use warp::WARP; @@ -145,7 +145,7 @@ mod inner { } prover .prove( - (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, &mut prover_state, witnesses.clone(), instances.clone(), diff --git a/src/error.rs b/src/error.rs index 681124c..49c4e38 100644 --- a/src/error.rs +++ b/src/error.rs @@ -33,6 +33,16 @@ pub enum ProverError { SpongeFish, #[error("Expected eval, got None")] EmptyEval, + #[error("instance batch must contain at least 2 instances; got {got}")] + InsufficientInstances { got: usize }, + #[error("instances.len() ({instances}) != witnesses.len() ({witnesses})")] + InstanceWitnessLengthMismatch { instances: usize, witnesses: usize }, + #[error("acc_witness.td.len() ({roots}) != acc_instance.rt.len() ({instances})")] + AccumulatorShapeMismatch { instances: usize, roots: usize }, + #[error("config parameter invalid: {reason}")] + ConfigParameterInvalid { reason: String }, + #[error("instance length mismatch: expected {expected}, got {got}")] + InstanceLengthMismatch { expected: usize, got: usize }, } impl From for ProverError { diff --git a/src/lib.rs b/src/lib.rs index e2c1448..44ff20d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,21 +1,24 @@ #![allow(clippy::assign_op_pattern)] // generated by SmallFpConfig derive macro use crate::error::WARPError; use crate::traits::AccumulationScheme; -use crate::types::{AccumulatorInstance, AccumulatorWitness, ProveResult, WARPParams, WARPProof}; +use crate::types::{ + AccumulatorInstance, AccumulatorWitness, ProveResult, WARPParams, WARPProof, WARPProverKey, + WARPVerifierKey, +}; use ark_codes::traits::LinearCode; use ark_ff::{Field, PrimeField}; use ark_mt::MerkleHasher; use ark_poly::{DenseMultilinearExtension, Polynomial}; use ark_std::log2; use config::WARPConfig; -use effsc::hypercube::{compute_hypercube_eq_evals, Ascending}; +use effsc::hypercube::compute_hypercube_eq_evals; use protocol::query::QueryIndices; use protocol::transcript::{absorb_instances, parse_statement}; use relations::{r1cs::R1CSConstraints, BundledPESAT}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; use utils::scale_and_sum; -use utils::{concat_slices, poly::eq_poly}; +use utils::concat_slices; pub mod config; pub mod constraints; @@ -92,8 +95,8 @@ where H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, { type Index = P; - type ProverKey = (P, usize, usize, usize); - type VerifierKey = (usize, usize, usize); + type ProverKey = WARPProverKey

    ; + type VerifierKey = WARPVerifierKey; type Instances = Vec>; type Witnesses = Vec>; @@ -106,7 +109,15 @@ where prover_state.prover_message(&F::from(m as u32)); prover_state.prover_message(&F::from(n as u32)); prover_state.prover_message(&F::from(k as u32)); - Ok(((index, m, n, k), (m, n, k))) + Ok(( + WARPProverKey { + index, + m, + n, + k, + }, + WARPVerifierKey { m, n, k }, + )) } #[tracing::instrument(name = "warp.prove", skip_all)] @@ -119,22 +130,44 @@ where acc_instance: AccumulatorInstance, acc_witness: AccumulatorWitness, ) -> ProveResult { - debug_assert!(instances.len() > 1); - debug_assert_eq!(witnesses.len(), instances.len()); - debug_assert_eq!(acc_witness.td.len(), acc_instance.rt.len()); + if instances.len() < 2 { + return Err(ProverError::InsufficientInstances { + got: instances.len(), + }); + } + if witnesses.len() != instances.len() { + return Err(ProverError::InstanceWitnessLengthMismatch { + instances: instances.len(), + witnesses: witnesses.len(), + }); + } + if acc_witness.td.len() != acc_instance.rt.len() { + return Err(ProverError::AccumulatorShapeMismatch { + instances: acc_instance.rt.len(), + roots: acc_witness.td.len(), + }); + } let (l1, l) = (self.params.config.l1, self.params.config.l); let l2 = l - l1; - debug_assert_eq!(l1 + l2, l); - debug_assert!(l.is_power_of_two()); + if !l.is_power_of_two() { + return Err(ProverError::ConfigParameterInvalid { + reason: format!("config.l = {l} is not a power of two"), + }); + } #[allow(non_snake_case)] - let (M, N, k) = (pk.1, pk.2, pk.3); + let (M, N, k) = (pk.m, pk.n, pk.k); let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); let n = self.params.code.code_len(); let log_n = log2(n) as usize; - debug_assert_eq!(instances[0].len(), N - k); + if instances[0].len() != N - k { + return Err(ProverError::InstanceLengthMismatch { + expected: N - k, + got: instances[0].len(), + }); + } absorb_instances(prover_state, &instances); acc_instance.absorb_into(prover_state); @@ -189,9 +222,7 @@ where )?; // Phase 3b: bundled η, ν₀, new commitment, absorb. - let beta_eq_evals = (0..M) - .map(|i| eq_poly(&tc_red.beta_tau, i)) - .collect::>(); + let beta_eq_evals = compute_hypercube_eq_evals(log_m, &tc_red.beta_tau); let eta = self .params .p @@ -320,7 +351,7 @@ where let l2 = l - l1; #[allow(non_snake_case)] - let (M, N, k) = (vk.0, vk.1, vk.2); + let (M, N, k) = (vk.m, vk.n, vk.k); let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); let n = self.params.code.code_len(); let log_n = log2(n) as usize; @@ -548,9 +579,7 @@ where // Bundled-evaluation check. let tau = &acc_instance.beta.0[0]; - let tau_zero_evader = Ascending::new(tau.len()) - .map(|p| eq_poly(tau, p.index)) - .collect::>(); + let tau_zero_evader = compute_hypercube_eq_evals(tau.len(), tau); let mut z = acc_instance.beta.1[0].clone(); z.extend(acc_witness.w[0].clone()); let computed_eta = self diff --git a/src/types.rs b/src/types.rs index 13a1e0c..fe58784 100644 --- a/src/types.rs +++ b/src/types.rs @@ -8,6 +8,23 @@ use crate::crypto::merkle::{WarpCommitted, WarpProof}; use crate::error::ProverError; use crate::relations::BundledPESAT; +/// Prover key — the relation index plus dimensions `(M, N, k)`. +#[derive(Clone)] +pub struct WARPProverKey

    { + pub index: P, + pub m: usize, + pub n: usize, + pub k: usize, +} + +/// Verifier key — dimensions only `(M, N, k)`. +#[derive(Clone, Copy)] +pub struct WARPVerifierKey { + pub m: usize, + pub n: usize, + pub k: usize, +} + // result of a prove call: (new accumulator instance + witness, proof) pub type ProveResult = Result< ( diff --git a/tests/integration_warp.rs b/tests/integration_warp.rs index d79abfa..f1ea087 100644 --- a/tests/integration_warp.rs +++ b/tests/integration_warp.rs @@ -35,7 +35,7 @@ use warp::relations::{ }; use warp::serialize::{AccInstanceSerializer, AccWitnessSerializer, ProofSerializer}; use warp::traits::AccumulationScheme; -use warp::types::{AccumulatorInstance, AccumulatorWitness}; +use warp::types::{AccumulatorInstance, AccumulatorWitness, WARPProverKey, WARPVerifierKey}; use warp::utils::poseidon; use warp::WARP; @@ -101,7 +101,7 @@ fn warp_test() { let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), _pf) = hash_chain_warp .prove( - (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, &mut prover_state, instances_witnesses.1.clone(), instances_witnesses.0.clone(), @@ -134,7 +134,7 @@ fn warp_test() { let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), pf) = hash_chain_warp .prove( - (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, &mut prover_state, instances_witnesses.1, instances_witnesses.0, @@ -160,7 +160,7 @@ fn warp_test() { .std_verifier(&narg_str); hash_chain_warp .verify( - (r1cs.m, r1cs.n, r1cs.k), + WARPVerifierKey { m: r1cs.m, n: r1cs.n, k: r1cs.k }, &mut verifier_state, acc_x.clone(), pf.clone(), @@ -254,7 +254,7 @@ fn warp_test_goldilocks() { let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), _pf) = hash_chain_warp .prove( - (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, &mut prover_state, instances_witnesses.1.clone(), instances_witnesses.0.clone(), @@ -289,7 +289,7 @@ fn warp_test_goldilocks() { let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), pf) = hash_chain_warp .prove( - (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, &mut prover_state, instances_witnesses.1, instances_witnesses.0, @@ -315,7 +315,7 @@ fn warp_test_goldilocks() { .std_verifier(&narg_str); hash_chain_warp .verify( - (r1cs.m, r1cs.n, r1cs.k), + WARPVerifierKey { m: r1cs.m, n: r1cs.n, k: r1cs.k }, &mut verifier_state, acc_x.clone(), pf.clone(), diff --git a/tests/profile_json.rs b/tests/profile_json.rs index 2aa78dc..389bac7 100644 --- a/tests/profile_json.rs +++ b/tests/profile_json.rs @@ -30,7 +30,7 @@ use warp::relations::{ BundledPESAT, Relation, ToPolySystem, }; use warp::traits::AccumulationScheme; -use warp::types::{AccumulatorInstance, AccumulatorWitness}; +use warp::types::{AccumulatorInstance, AccumulatorWitness, WARPProverKey}; use warp::utils::poseidon; use warp::WARP; @@ -108,7 +108,7 @@ fn json_layer_emits_phase_records() { hash_chain_warp .prove( - (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, &mut prover_state, witnesses, instances, diff --git a/tests/verifier_negative.rs b/tests/verifier_negative.rs index b61c80e..dcca4a1 100644 --- a/tests/verifier_negative.rs +++ b/tests/verifier_negative.rs @@ -42,7 +42,7 @@ use warp::relations::{ BundledPESAT, Relation, ToPolySystem, }; use warp::traits::AccumulationScheme; -use warp::types::{AccumulatorInstance, AccumulatorWitness, WARPProof}; +use warp::types::{AccumulatorInstance, AccumulatorWitness, WARPProof, WARPProverKey, WARPVerifierKey}; use warp::utils::poseidon; use warp::WARP; @@ -54,7 +54,7 @@ type WarpT = WARP, ReedSolomon, H>; /// to re-derive the verifier state. struct Fixture { warp: WarpT, - vk: (usize, usize, usize), + vk: WARPVerifierKey, acc_x: AccumulatorInstance, proof: WARPProof, narg_str: Vec, @@ -134,7 +134,7 @@ fn make_fixture() -> Fixture { let mut ps = ds.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), _) = w1 .prove( - (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, &mut ps, witnesses.clone(), instances.clone(), @@ -161,7 +161,7 @@ fn make_fixture() -> Fixture { let mut ps = ds.without_session().instance(&0u32).std_prover(); let ((acc_x, _acc_w), proof) = warp .prove( - (r1cs.clone(), r1cs.m, r1cs.n, r1cs.k), + WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, &mut ps, witnesses, instances, @@ -178,7 +178,7 @@ fn make_fixture() -> Fixture { Fixture { warp, - vk: (r1cs.m, r1cs.n, r1cs.k), + vk: WARPVerifierKey { m: r1cs.m, n: r1cs.n, k: r1cs.k }, acc_x, proof, narg_str: ps.narg_string().to_vec(), @@ -267,3 +267,81 @@ fn tampered_mu_raises_target() { acc_x.mu[0] += F::from(1u64); assert_err(fix.verify(acc_x, fix.proof.clone()), "Target"); } + +#[test] +fn prove_rejects_mismatched_instance_witness_lengths() { + use warp::error::ProverError; + + let l1 = 4; + let s = 8; + let t = 7; + let hash_chain_size = 4; + let mut rng = thread_rng(); + let poseidon_config = poseidon::initialize_poseidon_config::(); + let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + poseidon_config.clone(), + hash_chain_size, + )) + .unwrap(); + let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code = ReedSolomon::new(code_config); + + let (instances, witnesses): (Vec<_>, Vec<_>) = (0..l1) + .map(|_| { + let preimage = vec![F::rand(&mut rng)]; + let instance = HashChainInstance { + digest: compute_hash_chain::>( + &poseidon_config, + &preimage, + hash_chain_size, + ), + }; + let witness = HashChainWitness { + preimage, + _crhs_scheme: PhantomData::>, + }; + let relation = HashChainRelation::, CRHGadget<_>>::new( + instance, + witness, + (poseidon_config.clone(), hash_chain_size), + ); + (relation.x, relation.w) + }) + .unzip(); + + let warp_cfg = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); + let warp = WARP::, _, H>::new( + warp_cfg, + code, + r1cs.clone(), + Blake3FieldHasher::::new(), + ); + + let mut witnesses_short = witnesses; + witnesses_short.pop(); + + let ds = spongefish::domain_separator!("test::warp::prove_negative"); + let mut ps = ds.without_session().instance(&0u32).std_prover(); + let result = warp.prove( + WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, + &mut ps, + witnesses_short, + instances, + AccumulatorInstance::empty(), + AccumulatorWitness::empty(), + ); + + let err = result + .err() + .expect("prove must reject mismatched instance/witness lengths"); + match err { + ProverError::InstanceWitnessLengthMismatch { + instances, + witnesses, + } => { + assert_eq!(instances, l1); + assert_eq!(witnesses, l1 - 1); + } + other => panic!("expected InstanceWitnessLengthMismatch, got {other:?}"), + } +} From abe7fad8af9005f5ac91228d775b6f90dbf8af36 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 17:24:31 +0200 Subject: [PATCH 30/68] api: typed AccumulatorEntry + extend() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests no longer maintain six parallel Vecs to fold per-round single-entry accumulators into a multi-entry input. The internal layout stays SoA (parallel Vecs, no churn for the IOR / transcript code); the user-facing API gains: - AccumulatorEntry / AccumulatorWitnessEntry — AoS view of one accumulated claim - AccumulatorInstance::extend(self, other) -> Self / push_entry / from_entries - AccumulatorWitness::extend / push_entry / from_entries - len(), is_empty() Integration tests and the verifier-negative fixture become: let mut acc_x = AccumulatorInstance::empty(); let mut acc_w = AccumulatorWitness::empty(); for _ in 0..l1 { let ((new_x, new_w), _) = warp.prove(..., empty(), empty())?; acc_x = acc_x.extend(new_x); acc_w = acc_w.extend(new_w); } warp.prove(pk, ..., acc_x, acc_w) --- src/types.rs | 99 ++++++++++++++++++++++++++++++++++++++ tests/integration_warp.rs | 62 ++++++------------------ tests/verifier_negative.rs | 27 +++-------- 3 files changed, 120 insertions(+), 68 deletions(-) diff --git a/src/types.rs b/src/types.rs index fe58784..b058153 100644 --- a/src/types.rs +++ b/src/types.rs @@ -71,6 +71,66 @@ impl AccumulatorInstance { eta: vec![], } } + + /// Number of accumulated entries. + pub fn len(&self) -> usize { + self.rt.len() + } + + pub fn is_empty(&self) -> bool { + self.rt.is_empty() + } + + /// Builder-style merge: append every field of `other` to `self`. Used + /// by callers that fold per-round single-entry outputs into a + /// multi-entry input for the next round. + pub fn extend(mut self, other: Self) -> Self { + self.rt.extend(other.rt); + self.alpha.extend(other.alpha); + self.mu.extend(other.mu); + self.beta.0.extend(other.beta.0); + self.beta.1.extend(other.beta.1); + self.eta.extend(other.eta); + self + } + + /// Push a single typed entry. Pivots `AccumulatorEntry` (AoS) into the + /// internal parallel-Vec (SoA) layout. + pub fn push_entry(&mut self, entry: AccumulatorEntry) + where + H::Digest: Clone, + { + self.rt.push(entry.rt); + self.alpha.push(entry.alpha); + self.mu.push(entry.mu); + self.beta.0.push(entry.tau); + self.beta.1.push(entry.x); + self.eta.push(entry.eta); + } + + /// Construct from an explicit list of typed entries. + pub fn from_entries(entries: Vec>) -> Self + where + H::Digest: Clone, + { + let mut acc = Self::empty(); + for entry in entries { + acc.push_entry(entry); + } + acc + } +} + +/// One accumulated claim, AoS view of `AccumulatorInstance`. Use this +/// when constructing entries one at a time; pipe through +/// [`AccumulatorInstance::push_entry`] / [`AccumulatorInstance::from_entries`]. +pub struct AccumulatorEntry { + pub rt: H::Digest, + pub alpha: Vec, + pub mu: F, + pub tau: Vec, + pub x: Vec, + pub eta: F, } /// Accumulator witness — the private part of an accumulated claim. @@ -113,6 +173,45 @@ where w: vec![], } } + + pub fn len(&self) -> usize { + self.td.len() + } + + pub fn is_empty(&self) -> bool { + self.td.is_empty() + } + + /// Builder-style merge — counterpart of [`AccumulatorInstance::extend`]. + pub fn extend(mut self, other: Self) -> Self { + self.td.extend(other.td); + self.w.extend(other.w); + self + } + + pub fn push_entry(&mut self, entry: AccumulatorWitnessEntry) { + self.td.push(entry.td); + self.w.push(entry.w); + } + + pub fn from_entries(entries: Vec>) -> Self { + let mut acc = Self::empty(); + for entry in entries { + acc.push_entry(entry); + } + acc + } +} + +/// One witness entry for an accumulated claim — AoS counterpart of +/// `AccumulatorWitness`. +pub struct AccumulatorWitnessEntry +where + F: Field, + H: MerkleHasher>, +{ + pub td: crate::crypto::merkle::WarpCommitted, + pub w: Vec, } /// Proof produced by the WARP accumulation prover. diff --git a/tests/integration_warp.rs b/tests/integration_warp.rs index f1ea087..45f1e4d 100644 --- a/tests/integration_warp.rs +++ b/tests/integration_warp.rs @@ -92,14 +92,13 @@ fn warp_test() { Blake3FieldHasher::::new(), ); - let (mut acc_roots, mut acc_alphas, mut acc_mus, mut acc_taus, mut acc_xs, mut acc_eta) = - (vec![], vec![], vec![], vec![], vec![], vec![]); - let (mut acc_tds, mut acc_ws) = (vec![], vec![]); + let mut acc_x = AccumulatorInstance::empty(); + let mut acc_w = AccumulatorWitness::empty(); for _ in 0..l1 { let domainsep = spongefish::domain_separator!("test::warp"); let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); - let ((acc_x, acc_w), _pf) = hash_chain_warp + let ((new_x, new_w), _pf) = hash_chain_warp .prove( WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, &mut prover_state, @@ -109,15 +108,8 @@ fn warp_test() { AccumulatorWitness::empty(), ) .unwrap(); - acc_roots.push(acc_x.rt[0].clone()); - acc_alphas.push(acc_x.alpha[0].clone()); - acc_mus.push(acc_x.mu[0]); - acc_taus.push(acc_x.beta.0[0].clone()); - acc_xs.push(acc_x.beta.1[0].clone()); - acc_eta.push(acc_x.eta[0]); - - acc_tds.push(acc_w.td[0].clone()); - acc_ws.push(acc_w.w[0].clone()); + acc_x = acc_x.extend(new_x); + acc_w = acc_w.extend(new_w); } let domainsep = spongefish::domain_separator!("test::warp"); @@ -138,17 +130,8 @@ fn warp_test() { &mut prover_state, instances_witnesses.1, instances_witnesses.0, - AccumulatorInstance { - rt: acc_roots, - alpha: acc_alphas, - mu: acc_mus, - beta: (acc_taus, acc_xs), - eta: acc_eta, - }, - AccumulatorWitness { - td: acc_tds, - w: acc_ws, - }, + acc_x, + acc_w, ) .unwrap(); @@ -245,14 +228,13 @@ fn warp_test_goldilocks() { Blake3FieldHasher::::new(), ); - let (mut acc_roots, mut acc_alphas, mut acc_mus, mut acc_taus, mut acc_xs, mut acc_eta) = - (vec![], vec![], vec![], vec![], vec![], vec![]); - let (mut acc_tds, mut acc_ws) = (vec![], vec![]); + let mut acc_x = AccumulatorInstance::empty(); + let mut acc_w = AccumulatorWitness::empty(); for _ in 0..l1 { let domainsep = spongefish::domain_separator!("test::warp"); let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); - let ((acc_x, acc_w), _pf) = hash_chain_warp + let ((new_x, new_w), _pf) = hash_chain_warp .prove( WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, &mut prover_state, @@ -262,15 +244,8 @@ fn warp_test_goldilocks() { AccumulatorWitness::empty(), ) .unwrap(); - acc_roots.push(acc_x.rt[0].clone()); - acc_alphas.push(acc_x.alpha[0].clone()); - acc_mus.push(acc_x.mu[0]); - acc_taus.push(acc_x.beta.0[0].clone()); - acc_xs.push(acc_x.beta.1[0].clone()); - acc_eta.push(acc_x.eta[0]); - - acc_tds.push(acc_w.td[0].clone()); - acc_ws.push(acc_w.w[0].clone()); + acc_x = acc_x.extend(new_x); + acc_w = acc_w.extend(new_w); } let domainsep = spongefish::domain_separator!("test::warp"); @@ -293,17 +268,8 @@ fn warp_test_goldilocks() { &mut prover_state, instances_witnesses.1, instances_witnesses.0, - AccumulatorInstance { - rt: acc_roots, - alpha: acc_alphas, - mu: acc_mus, - beta: (acc_taus, acc_xs), - eta: acc_eta, - }, - AccumulatorWitness { - td: acc_tds, - w: acc_ws, - }, + acc_x, + acc_w, ) .unwrap(); diff --git a/tests/verifier_negative.rs b/tests/verifier_negative.rs index dcca4a1..a39ac4b 100644 --- a/tests/verifier_negative.rs +++ b/tests/verifier_negative.rs @@ -125,14 +125,13 @@ fn make_fixture() -> Fixture { Blake3FieldHasher::::new(), ); - let (mut roots, mut alphas, mut mus, mut taus, mut xs, mut etas) = - (vec![], vec![], vec![], vec![], vec![], vec![]); - let (mut tds, mut ws) = (vec![], vec![]); + let mut acc_x = AccumulatorInstance::empty(); + let mut acc_w = AccumulatorWitness::empty(); for _ in 0..l1 { let ds = spongefish::domain_separator!("test::warp::negative"); let mut ps = ds.without_session().instance(&0u32).std_prover(); - let ((acc_x, acc_w), _) = w1 + let ((new_x, new_w), _) = w1 .prove( WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, &mut ps, @@ -142,14 +141,8 @@ fn make_fixture() -> Fixture { AccumulatorWitness::empty(), ) .unwrap(); - roots.push(acc_x.rt[0].clone()); - alphas.push(acc_x.alpha[0].clone()); - mus.push(acc_x.mu[0]); - taus.push(acc_x.beta.0[0].clone()); - xs.push(acc_x.beta.1[0].clone()); - etas.push(acc_x.eta[0]); - tds.push(acc_w.td[0].clone()); - ws.push(acc_w.w[0].clone()); + acc_x = acc_x.extend(new_x); + acc_w = acc_w.extend(new_w); } // Phase 2: the "real" prove with l2 > 0 accumulated instances. @@ -165,14 +158,8 @@ fn make_fixture() -> Fixture { &mut ps, witnesses, instances, - AccumulatorInstance { - rt: roots, - alpha: alphas, - mu: mus, - beta: (taus, xs), - eta: etas, - }, - AccumulatorWitness { td: tds, w: ws }, + acc_x, + acc_w, ) .unwrap(); From cc0d3563e9d7df96138f946abea9059216efe6a5 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 17:32:07 +0200 Subject: [PATCH 31/68] perf: parallelize R1CS::evaluate_bundled with rayon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the explicit "TODO: multithread this" — bundled-eval was the single-threaded sequential fold over m constraints. Each constraint does 3 sparse dot products against z, all independent. Pre-checks zero_evader_evals.len() >= m once so the inner closure can direct-index without re-checking. Bounds for index 0..m are guaranteed by R1CS construction. --- Cargo.toml | 1 + src/relations/r1cs/mod.rs | 24 ++++++++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ea15ed9..00f799d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ ark-relations = "0.6.0" ark-serialize = "0.6.0" ark-std = "0.6.0" blake3 = "1.5.0" +rayon = "1" serde = { version = "1.0.0", features = ["derive"] } serde_json = "1.0" spongefish = { version = "0.7.0", features = ["ark-ff"] } diff --git a/src/relations/r1cs/mod.rs b/src/relations/r1cs/mod.rs index a23ee4f..d57e627 100644 --- a/src/relations/r1cs/mod.rs +++ b/src/relations/r1cs/mod.rs @@ -2,7 +2,7 @@ pub mod hashchain; use ark_ff::Field; use ark_relations::gr1cs::ConstraintSystemRef; -use effsc::hypercube::Ascending; +use rayon::prelude::*; use crate::error::WARPError; @@ -99,15 +99,19 @@ impl BundledPESAT for R1CS { type Constraints = R1CSConstraints; fn evaluate_bundled(&self, zero_evader_evals: &[F], z: &[F]) -> Result { - // TODO: multithread this - Ascending::new(self.log_m).try_fold(F::ZERO, |acc, p| { - let index = p.index; - let eq_tau_i = *zero_evader_evals - .get(index) - .ok_or(WARPError::ZeroEvaderSize(zero_evader_evals.len(), index))?; - let p_i = self.eval_p_i(z, index)?; - Ok(acc + eq_tau_i * p_i) - }) + if zero_evader_evals.len() < self.m { + return Err(WARPError::ZeroEvaderSize( + zero_evader_evals.len(), + self.m - 1, + )); + } + (0..self.m) + .into_par_iter() + .map(|i| -> Result { + let p_i = self.eval_p_i(z, i)?; + Ok(zero_evader_evals[i] * p_i) + }) + .try_reduce(|| F::ZERO, |acc, x| Ok(acc + x)) } fn config(&self) -> Self::Config { From dc8e4ce32220dd0cc248608c91e194f5672a56be Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 20:24:32 +0200 Subject: [PATCH 32/68] refactor: BatchingStatement::from_phase_outputs as single source of truth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The orchestrator built zetas_prefix in two places (lib.rs prover side and verifier side) with byte-identical logic. Push the assembly into a constructor on BatchingStatement so both call sites go through one function. Drift between prover and verifier on this layout becomes structurally impossible — same pattern IOR::reduce_statement already enforces inside phases. --- src/lib.rs | 48 +++++++++++---------------------- src/protocol/phases/batching.rs | 32 ++++++++++++++++++++++ 2 files changed, 47 insertions(+), 33 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 44ff20d..94b0819 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -265,26 +265,17 @@ where let queries = QueryIndices::::sample(prover_state, log_n, self.params.config.t); // Phase 3d: batching sumcheck. - let mut zetas: Vec> = - Vec::with_capacity(1 + self.params.config.s + self.params.config.t); - zetas.push(tc_red.zeta_0.clone()); - for chunk in ood_red.samples_flat.chunks(log_n) { - zetas.push(chunk.to_vec()); - } - for q in &queries.evaluation_points { - zetas.push(q.clone()); - } - let batching_phase = Batching::::new(); let (batching_red, _, batching_red_wit) = batching_phase.prove( prover_state, - &BatchingStatement { - zetas_prefix: zetas, - s: self.params.config.s, - t: self.params.config.t, + &BatchingStatement::from_phase_outputs( + tc_red.zeta_0.clone(), + &ood_red.samples_flat, + &queries.evaluation_points, + self.params.config.s, + self.params.config.t, log_n, - _phantom: PhantomData, - }, + ), &(), &BatchingProverInputs { oracle: &tc_red_wit.f, @@ -508,27 +499,18 @@ where nus.push(nu_st); } - // 9. Build zetas_prefix; Batching::verify. - let mut zetas: Vec> = - Vec::with_capacity(1 + self.params.config.s + self.params.config.t); - zetas.push(tc_red.zeta_0.clone()); - for chunk in ood_red.samples_flat.chunks(log_n) { - zetas.push(chunk.to_vec()); - } - for pt in &queries.evaluation_points { - zetas.push(pt.clone()); - } - + // 9. Batching::verify. let batching_phase = Batching::::new(); let (batching_red, _) = batching_phase.verify( verifier_state, - &BatchingStatement { - zetas_prefix: zetas, - s: self.params.config.s, - t: self.params.config.t, + &BatchingStatement::from_phase_outputs( + tc_red.zeta_0.clone(), + &ood_red.samples_flat, + &queries.evaluation_points, + self.params.config.s, + self.params.config.t, log_n, - _phantom: PhantomData, - }, + ), &BatchingVerifierInputs { nus, acc_mu: acc_mu_first, diff --git a/src/protocol/phases/batching.rs b/src/protocol/phases/batching.rs index a220f28..0bc01b8 100644 --- a/src/protocol/phases/batching.rs +++ b/src/protocol/phases/batching.rs @@ -96,6 +96,38 @@ pub struct BatchingStatement { pub _phantom: std::marker::PhantomData, } +impl BatchingStatement { + /// Single source of truth for the `zetas_prefix` shape — both the + /// prover and verifier orchestrators construct their `BatchingStatement` + /// through this. Drift between sides becomes structurally impossible. + /// + /// Layout: `[ζ_0, ood_chunk_0, …, ood_chunk_{s-1}, query_0, …, query_{t-1}]`. + pub fn from_phase_outputs( + zeta_0: Vec, + ood_samples_flat: &[F], + query_eval_points: &[Vec], + s: usize, + t: usize, + log_n: usize, + ) -> Self { + let mut zetas: Vec> = Vec::with_capacity(1 + s + t); + zetas.push(zeta_0); + for chunk in ood_samples_flat.chunks(log_n) { + zetas.push(chunk.to_vec()); + } + for q in query_eval_points { + zetas.push(q.clone()); + } + Self { + zetas_prefix: zetas, + s, + t, + log_n, + _phantom: std::marker::PhantomData, + } + } +} + pub struct BatchingProverInputs<'a, F: Field> { pub oracle: &'a Oracle, } From 22d3b518dac180d542b825823e79b52f3be65fd0 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 20:27:40 +0200 Subject: [PATCH 33/68] api: hide PhantomData fields and add HashChainWitness::new WARPParams._f gains pub(crate) (only built inside WARP::new). Tests no longer needed to import std::marker::PhantomData just to construct it. HashChainWitness adds a `new(preimage)` constructor; _crhs_scheme stays pub(crate) so the relation/synthesizer modules continue to construct literals while external callers go through the constructor. Updates all integration test, profile_json, verifier_negative, example, and bench callsites; removes the now-unused PhantomData imports. --- benches/utils/hash_chain.rs | 7 +------ examples/profile_phases.rs | 6 +----- src/relations/r1cs/hashchain/witness.rs | 15 ++++++++++++++- src/types.rs | 2 +- tests/integration_warp.rs | 12 ++---------- tests/profile_json.rs | 7 +------ tests/verifier_negative.rs | 12 ++---------- 7 files changed, 22 insertions(+), 39 deletions(-) diff --git a/benches/utils/hash_chain.rs b/benches/utils/hash_chain.rs index 5c5517e..7cfd6ef 100644 --- a/benches/utils/hash_chain.rs +++ b/benches/utils/hash_chain.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use ark_crypto_primitives::{ crh::poseidon::{constraints::CRHGadget, CRH}, sponge::{poseidon::PoseidonConfig, Absorb}, @@ -37,10 +35,7 @@ pub fn get_hashchain_instance_witness_pairs( let instance = HashChainInstance { digest: compute_hash_chain::>(poseidon_config, &preimage, hashchain_size), }; - let witness = HashChainWitness { - preimage, - _crhs_scheme: PhantomData::>, - }; + let witness = HashChainWitness::>::new(preimage); let relation = HashChainRelation::, CRHGadget<_>>::new( instance, witness, diff --git a/examples/profile_phases.rs b/examples/profile_phases.rs index 596dfbf..7eef227 100644 --- a/examples/profile_phases.rs +++ b/examples/profile_phases.rs @@ -31,7 +31,6 @@ fn main() { #[cfg(feature = "profile")] mod inner { use std::io::{self, Write}; - use std::marker::PhantomData; use std::sync::{Arc, Mutex}; use ark_codes::reed_solomon::config::ReedSolomonConfig; @@ -111,10 +110,7 @@ mod inner { hash_chain_size, ), }; - let witness = HashChainWitness { - preimage, - _crhs_scheme: PhantomData::>, - }; + let witness = HashChainWitness::>::new(preimage); let relation = HashChainRelation::, CRHGadget<_>>::new( instance, witness, diff --git a/src/relations/r1cs/hashchain/witness.rs b/src/relations/r1cs/hashchain/witness.rs index 5144bf1..a716a03 100644 --- a/src/relations/r1cs/hashchain/witness.rs +++ b/src/relations/r1cs/hashchain/witness.rs @@ -10,7 +10,20 @@ where H: CRHScheme, { pub preimage: Vec, - pub _crhs_scheme: PhantomData, + pub(crate) _crhs_scheme: PhantomData, +} + +impl HashChainWitness +where + F: Field + PrimeField, + H: CRHScheme, +{ + pub fn new(preimage: Vec) -> Self { + Self { + preimage, + _crhs_scheme: PhantomData, + } + } } impl Clone for HashChainWitness diff --git a/src/types.rs b/src/types.rs index b058153..0f0b967 100644 --- a/src/types.rs +++ b/src/types.rs @@ -36,7 +36,7 @@ pub type ProveResult = Result< /// Protocol parameters for WARP — the shared configuration used by all IOR phases. pub struct WARPParams, C: LinearCode + Clone, H: MerkleHasher> { - pub _f: PhantomData, + pub(crate) _f: PhantomData, pub config: WARPConfig, pub code: C, pub p: P, diff --git a/tests/integration_warp.rs b/tests/integration_warp.rs index 45f1e4d..bc525e0 100644 --- a/tests/integration_warp.rs +++ b/tests/integration_warp.rs @@ -12,8 +12,6 @@ //! differ enough that a single generic function would need a long //! `where` clause for marginal DRY gain. -use std::marker::PhantomData; - use ark_bls12_381::Fr as BLS12_381; use ark_codes::{ reed_solomon::{config::ReedSolomonConfig, ReedSolomon}, @@ -65,10 +63,7 @@ fn warp_test() { hash_chain_size, ), }; - let witness = HashChainWitness { - preimage, - _crhs_scheme: PhantomData::>, - }; + let witness = HashChainWitness::>::new(preimage); let relation = HashChainRelation::, CRHGadget<_>>::new( instance, witness, @@ -200,10 +195,7 @@ fn warp_test_goldilocks() { hash_chain_size, ), }; - let witness = HashChainWitness { - preimage, - _crhs_scheme: PhantomData::>, - }; + let witness = HashChainWitness::>::new(preimage); let relation = HashChainRelation::, CRHGadget<_>>::new( instance, witness, diff --git a/tests/profile_json.rs b/tests/profile_json.rs index 389bac7..1b195c4 100644 --- a/tests/profile_json.rs +++ b/tests/profile_json.rs @@ -19,8 +19,6 @@ use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; use ark_mt::blake3::Blake3FieldHasher; use ark_std::rand::thread_rng; use ark_std::UniformRand; -use std::marker::PhantomData; - use warp::config::WARPConfig; use warp::relations::{ r1cs::{ @@ -82,10 +80,7 @@ fn json_layer_emits_phase_records() { hash_chain_size, ), }; - let witness = HashChainWitness { - preimage, - _crhs_scheme: PhantomData::>, - }; + let witness = HashChainWitness::>::new(preimage); let relation = HashChainRelation::, CRHGadget<_>>::new( instance, witness, diff --git a/tests/verifier_negative.rs b/tests/verifier_negative.rs index a39ac4b..c639c94 100644 --- a/tests/verifier_negative.rs +++ b/tests/verifier_negative.rs @@ -20,8 +20,6 @@ //! `SpongeFish`. //! - `SumcheckRound` is not raised from any code path right now. -use std::marker::PhantomData; - use ark_bls12_381::Fr as BLS12_381; use ark_codes::{ reed_solomon::{config::ReedSolomonConfig, ReedSolomon}, @@ -102,10 +100,7 @@ fn make_fixture() -> Fixture { hash_chain_size, ), }; - let witness = HashChainWitness { - preimage, - _crhs_scheme: PhantomData::>, - }; + let witness = HashChainWitness::>::new(preimage); let relation = HashChainRelation::, CRHGadget<_>>::new( instance, witness, @@ -283,10 +278,7 @@ fn prove_rejects_mismatched_instance_witness_lengths() { hash_chain_size, ), }; - let witness = HashChainWitness { - preimage, - _crhs_scheme: PhantomData::>, - }; + let witness = HashChainWitness::>::new(preimage); let relation = HashChainRelation::, CRHGadget<_>>::new( instance, witness, From 0bcd765f0a28cc4bee76e4194bfb6b33a52b8da9 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 20:30:54 +0200 Subject: [PATCH 34/68] serialize: drop assert-len-1 panics, replace wrappers with direct impls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AccInstanceSerializer / ProofSerializer wrappers asserted single-entry inputs at runtime — fine for the orchestrator's outputs (always single-entry), but awkward as a public API: it advertised a multi-entry shape that would panic. Replace with manual CanonicalSerialize impls on AccumulatorInstance and WARPProof in separate impl blocks, so the bound is only paid by the size-printing path (the IOR/orchestrator stays bound-free). Drops the f_i_x_j -> shift_query_answers rename. AccumulatorWitness keeps its custom path (acc_witness_size helper) — td (the Merkle tree) still shouldn't ship; only w does. --- src/serialize.rs | 147 +++++++++++++++++++++----------------- tests/integration_warp.rs | 34 ++------- 2 files changed, 90 insertions(+), 91 deletions(-) diff --git a/src/serialize.rs b/src/serialize.rs index 9f0e7db..d72e0ea 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -1,93 +1,112 @@ -use ark_ff::{Field, PrimeField}; +use ark_ff::Field; use ark_mt::MerkleHasher; -use ark_serialize::CanonicalSerialize; +use ark_serialize::{CanonicalSerialize, Compress, SerializationError, Valid, Write}; use crate::crypto::merkle::WarpProof; use crate::types::{AccumulatorInstance, AccumulatorWitness, WARPProof}; -#[derive(CanonicalSerialize)] -pub struct AccWitnessSerializer { - pub w: Vec, - _h: std::marker::PhantomData, -} - -impl>> AccWitnessSerializer { - pub fn new(acc_witness: AccumulatorWitness) -> Self { - assert_eq!(acc_witness.td.len(), 1); - assert_eq!(acc_witness.w.len(), 1); - Self { - w: acc_witness.w.into_iter().next().unwrap(), - _h: std::marker::PhantomData, - } - } -} +// `AccumulatorInstance` and `WARPProof` carry generic associated types +// (`H::Digest`, `WarpProof`) whose serializability is not implied by +// `H: MerkleHasher`. Putting the `CanonicalSerialize` bound in a +// separate `impl` block (rather than on the struct) keeps the bulk of +// the IOR / orchestrator code free of that dep — only the size-printing +// path pulls it in. -#[derive(CanonicalSerialize)] -pub struct AccInstanceSerializer +impl CanonicalSerialize for AccumulatorInstance where + F: Field + CanonicalSerialize, + H: MerkleHasher, H::Digest: CanonicalSerialize, { - pub rt: H::Digest, - pub alpha: Vec, - pub mu: F, - pub beta: (Vec, Vec), - pub eta: F, + fn serialize_with_mode( + &self, + mut writer: W, + compress: Compress, + ) -> Result<(), SerializationError> { + self.rt.serialize_with_mode(&mut writer, compress)?; + self.alpha.serialize_with_mode(&mut writer, compress)?; + self.mu.serialize_with_mode(&mut writer, compress)?; + self.beta.0.serialize_with_mode(&mut writer, compress)?; + self.beta.1.serialize_with_mode(&mut writer, compress)?; + self.eta.serialize_with_mode(&mut writer, compress)?; + Ok(()) + } + + fn serialized_size(&self, compress: Compress) -> usize { + self.rt.serialized_size(compress) + + self.alpha.serialized_size(compress) + + self.mu.serialized_size(compress) + + self.beta.0.serialized_size(compress) + + self.beta.1.serialized_size(compress) + + self.eta.serialized_size(compress) + } } -impl AccInstanceSerializer +impl Valid for AccumulatorInstance where + F: Field + CanonicalSerialize, + H: MerkleHasher, H::Digest: CanonicalSerialize, { - pub fn new(acc_instance: AccumulatorInstance) -> Self { - assert_eq!(acc_instance.rt.len(), 1); - assert_eq!(acc_instance.alpha.len(), 1); - assert_eq!(acc_instance.mu.len(), 1); - assert_eq!(acc_instance.beta.0.len(), 1); - assert_eq!(acc_instance.beta.1.len(), 1); - assert_eq!(acc_instance.eta.len(), 1); - let beta = ( - acc_instance.beta.0.into_iter().next().unwrap(), - acc_instance.beta.1.into_iter().next().unwrap(), - ); - Self { - rt: acc_instance.rt.into_iter().next().unwrap(), - alpha: acc_instance.alpha.into_iter().next().unwrap(), - mu: acc_instance.mu[0], - beta, - eta: acc_instance.eta[0], - } + fn check(&self) -> Result<(), SerializationError> { + Ok(()) } } -#[derive(CanonicalSerialize)] -pub struct ProofSerializer +impl CanonicalSerialize for WARPProof where + F: Field + CanonicalSerialize, + H: MerkleHasher, H::Digest: CanonicalSerialize, WarpProof: CanonicalSerialize, { - pub rt_0: H::Digest, - pub mu_i: Vec, - pub nu_0: F, - pub nu_i: Vec, - pub auth_0: WarpProof, - pub auth_j: Vec>, - pub f_i_x_j: Vec>, + fn serialize_with_mode( + &self, + mut writer: W, + compress: Compress, + ) -> Result<(), SerializationError> { + self.rt_0.serialize_with_mode(&mut writer, compress)?; + self.mu_i.serialize_with_mode(&mut writer, compress)?; + self.nu_0.serialize_with_mode(&mut writer, compress)?; + self.nu_i.serialize_with_mode(&mut writer, compress)?; + self.auth_0.serialize_with_mode(&mut writer, compress)?; + self.auth_j.serialize_with_mode(&mut writer, compress)?; + self.shift_query_answers + .serialize_with_mode(&mut writer, compress)?; + Ok(()) + } + + fn serialized_size(&self, compress: Compress) -> usize { + self.rt_0.serialized_size(compress) + + self.mu_i.serialized_size(compress) + + self.nu_0.serialized_size(compress) + + self.nu_i.serialized_size(compress) + + self.auth_0.serialized_size(compress) + + self.auth_j.serialized_size(compress) + + self.shift_query_answers.serialized_size(compress) + } } -impl ProofSerializer +impl Valid for WARPProof where + F: Field + CanonicalSerialize, + H: MerkleHasher, H::Digest: CanonicalSerialize, WarpProof: CanonicalSerialize, { - pub fn new(proof: WARPProof) -> Self { - Self { - rt_0: proof.rt_0, - mu_i: proof.mu_i, - nu_0: proof.nu_0, - nu_i: proof.nu_i, - auth_0: proof.auth_0, - auth_j: proof.auth_j, - f_i_x_j: proof.shift_query_answers, - } + fn check(&self) -> Result<(), SerializationError> { + Ok(()) } } + +/// `AccumulatorWitness` deliberately drops `td` (the full Merkle tree) +/// from the serialized form: only `w` ships across the wire — the tree +/// is reconstructable by re-encoding `w`. Used for proof-size reporting, +/// not on-the-wire serialization. +pub fn acc_witness_size(acc_witness: &AccumulatorWitness, compress: Compress) -> usize +where + F: Field + CanonicalSerialize, + H: MerkleHasher>, +{ + acc_witness.w.serialized_size(compress) +} diff --git a/tests/integration_warp.rs b/tests/integration_warp.rs index bc525e0..6399105 100644 --- a/tests/integration_warp.rs +++ b/tests/integration_warp.rs @@ -31,7 +31,7 @@ use warp::relations::{ }, BundledPESAT, Relation, ToPolySystem, }; -use warp::serialize::{AccInstanceSerializer, AccWitnessSerializer, ProofSerializer}; +use warp::serialize::acc_witness_size; use warp::traits::AccumulationScheme; use warp::types::{AccumulatorInstance, AccumulatorWitness, WARPProverKey, WARPVerifierKey}; use warp::utils::poseidon; @@ -148,22 +148,9 @@ fn warp_test() { .decide(acc_w.clone(), acc_x.clone()) .unwrap(); - let acc_x_to_serde = AccInstanceSerializer::<_, Blake3FieldHasher>::new(acc_x); - let acc_w_to_serde = AccWitnessSerializer::<_, Blake3FieldHasher>::new(acc_w); - let proof_to_serde = ProofSerializer::new(pf); - - println!( - "acc_x size: {}", - acc_x_to_serde.serialized_size(Compress::Yes) - ); - println!( - "acc_w size: {}", - acc_w_to_serde.serialized_size(Compress::Yes) - ); - println!( - "proof size: {}", - proof_to_serde.serialized_size(Compress::Yes) - ); + println!("acc_x size: {}", acc_x.serialized_size(Compress::Yes)); + println!("acc_w size: {}", acc_witness_size(&acc_w, Compress::Yes)); + println!("proof size: {}", pf.serialized_size(Compress::Yes)); println!("narg_str size: {}", narg_str.len()); } @@ -283,21 +270,14 @@ fn warp_test_goldilocks() { .decide(acc_w.clone(), acc_x.clone()) .unwrap(); - let acc_x_to_serde = AccInstanceSerializer::<_, Blake3FieldHasher>::new(acc_x); - let acc_w_to_serde = AccWitnessSerializer::<_, Blake3FieldHasher>::new(acc_w); - let proof_to_serde = ProofSerializer::new(pf); - println!( "Goldilocks acc_x size: {}", - acc_x_to_serde.serialized_size(Compress::Yes) + acc_x.serialized_size(Compress::Yes) ); println!( "Goldilocks acc_w size: {}", - acc_w_to_serde.serialized_size(Compress::Yes) - ); - println!( - "Goldilocks proof size: {}", - proof_to_serde.serialized_size(Compress::Yes) + acc_witness_size(&acc_w, Compress::Yes) ); + println!("Goldilocks proof size: {}", pf.serialized_size(Compress::Yes)); println!("Goldilocks narg_str size: {}", narg_str.len()); } From 247c551c056caf607ec7e867205c496342ef6d30 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 20:32:19 +0200 Subject: [PATCH 35/68] soundness: implement R1CS::description so index() actually binds matrices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Was todo!() — index() would panic on call. Now serializes the concrete (A, B, C) matrix triple via SerializableConstraintMatrices, so two R1CS systems with identical (m, n, k) but different constraints absorb to distinct bytes (and therefore distinct transcripts). Closes the soundness hole flagged in the orchestrator review: any downstream protocol that trusts index() to bind the relation now does. Note this only kicks in when callers actually use index() to build their ProverKey/VerifierKey. Manual construction via WARPProverKey { ... } still bypasses the binding — a separate API tightening for future work. --- src/relations/r1cs/mod.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/relations/r1cs/mod.rs b/src/relations/r1cs/mod.rs index d57e627..2b23c04 100644 --- a/src/relations/r1cs/mod.rs +++ b/src/relations/r1cs/mod.rs @@ -5,6 +5,7 @@ use ark_relations::gr1cs::ConstraintSystemRef; use rayon::prelude::*; use crate::error::WARPError; +use crate::relations::SerializableConstraintMatrices; use super::BundledPESAT; @@ -119,7 +120,26 @@ impl BundledPESAT for R1CS { } fn description(&self) -> Vec { - todo!() + // Serializes the *concrete matrix triple* so two R1CS systems with + // identical (m, n, k) but different constraints absorb to different + // bytes (and therefore distinct transcripts). Without this, + // `WARP::index` would only commit to dimensions — a soundness hole + // in any downstream protocol that trusts `index()` to bind the + // relation. + let a: Vec> = self.p.iter().map(|(a, _, _)| a.clone()).collect(); + let b: Vec> = self.p.iter().map(|(_, b, _)| b.clone()).collect(); + let c: Vec> = self.p.iter().map(|(_, _, c)| c.clone()).collect(); + let serializable = SerializableConstraintMatrices { + num_instance_variables: self.n - self.k, + num_witness_variables: self.k, + num_constraints: self.m, + a: SerializableConstraintMatrices::serialize_nested_field(a), + b: SerializableConstraintMatrices::serialize_nested_field(b), + c: SerializableConstraintMatrices::serialize_nested_field(c), + }; + serde_json::to_string(&serializable) + .expect("matrix serialization is infallible") + .into_bytes() } fn constraints(&self) -> &Self::Constraints { From 0c51b61f09d2e5feb6ab120e97f2b159ba5b572d Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 20:33:24 +0200 Subject: [PATCH 36/68] api: add prelude module + drop unused error variants - New `warp::prelude` re-exports the items every consumer needs (WARPConfig, AccumulationScheme, WARPProof, WARPProverKey, WARPVerifierKey, AccumulatorInstance/Witness/Entry, the error types, WARP). Replaces having to `use warp::config::WARPConfig;` + `warp::traits::AccumulationScheme;` + `warp::types::{...};` + `warp::error::{...};` etc. - WARPError loses DecodeFailed, UnsatisfiedMultiConstraints, CodewordSize (no callers). ProverError loses EmptyEval (also no callers). --- src/error.rs | 8 -------- src/lib.rs | 13 +++++++++++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/error.rs b/src/error.rs index 49c4e38..f451298 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,12 +17,6 @@ pub enum WARPError { ZeroEvaderSize(usize, usize), #[error("LC does not exist")] R1CSNonExistingLC, - #[error("Error decoding codeword")] - DecodeFailed, - #[error("Bundled PESAT eval returned {0}, multilinear evals returned {1}")] - UnsatisfiedMultiConstraints(bool, bool), - #[error("f.len() is {0}, but tried accessing at {1}")] - CodewordSize(usize, usize), } #[derive(Error, Debug)] @@ -31,8 +25,6 @@ pub enum ProverError { ArkError(#[from] Error), #[error("Spongefish verification error")] SpongeFish, - #[error("Expected eval, got None")] - EmptyEval, #[error("instance batch must contain at least 2 instances; got {got}")] InsufficientInstances { got: usize }, #[error("instances.len() ({instances}) != witnesses.len() ({witnesses})")] diff --git a/src/lib.rs b/src/lib.rs index 94b0819..036266d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,6 +33,19 @@ pub mod traits; pub mod types; pub mod utils; +/// Re-exports the handful of items every WARP consumer needs. Use as +/// `use warp::prelude::*;`. +pub mod prelude { + pub use crate::config::WARPConfig; + pub use crate::error::{DeciderError, ProverError, VerifierError, WARPError}; + pub use crate::traits::AccumulationScheme; + pub use crate::types::{ + AccumulatorEntry, AccumulatorInstance, AccumulatorWitness, AccumulatorWitnessEntry, + WARPProof, WARPProverKey, WARPVerifierKey, + }; + pub use crate::WARP; +} + use crate::crypto::merkle::warp_scheme; use error::{DeciderError, ProverError, VerifierError}; use protocol::phases::{ From 84b15995db07a1bbe078570c570756fddc5a3e6a Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 20:33:53 +0200 Subject: [PATCH 37/68] docs: real README with quick-start example Was two lines. Now covers status, a runnable quick-start showing the typical prove/extend/decide loop using the new prelude + accumulator helpers, repo layout, and how to run tests / benches / the profile example. --- README.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 23a13b4..adc0dd7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,73 @@ 🌀 WARP 🌀 -Implementation repo for [WARP](https://eprint.iacr.org/2025/753). +Implementation repo for [WARP](https://eprint.iacr.org/2025/753) — an +accumulation scheme over R1CS with code-based commitments. +## Status +Research artifact. Reproduces the protocol from the paper. Not packaged +for crates.io (depends on git branches of `ark-vc`, `ark-codes`, and +`effsc`). + +## Quick start + +```rust +use warp::prelude::*; +use warp::utils::poseidon; +use warp::relations::{r1cs::{R1CS, hashchain::HashChainRelation}, BundledPESAT, ToPolySystem}; +use ark_codes::{reed_solomon::{ReedSolomon, config::ReedSolomonConfig}, traits::LinearCode}; +use ark_crypto_primitives::crh::poseidon::{CRH, constraints::CRHGadget}; +use ark_mt::blake3::Blake3FieldHasher; +use ark_bls12_381::Fr as F; + +// 1. Build the relation (here: a hash chain of length 10). +let poseidon = poseidon::initialize_poseidon_config::(); +let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&(poseidon, 10))?; + +// 2. Pick a code and a hasher. +let code = ReedSolomon::new(ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two())); +let hasher = Blake3FieldHasher::::new(); + +// 3. Configure WARP and instantiate. +// `l1` = fresh-instance batch size, `l` = total accumulator capacity, `s`/`t` = OOD/shift queries. +let cfg = WARPConfig::new(/*l1*/ 4, /*l*/ 4, /*s*/ 8, /*t*/ 7, r1cs.config(), code.code_len()); +let warp = WARP::new(cfg, code, r1cs.clone(), hasher); + +// 4. Fold a stream of fresh (instance, witness) batches into the accumulator. +let mut acc_x = AccumulatorInstance::empty(); +let mut acc_w = AccumulatorWitness::empty(); +let pk = WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }; +for batch in batches { + let mut prover_state = /* spongefish prover state */; + let ((new_x, new_w), _proof) = warp.prove( + pk.clone(), &mut prover_state, batch.witnesses, batch.instances, + AccumulatorInstance::empty(), AccumulatorWitness::empty(), + )?; + acc_x = acc_x.extend(new_x); + acc_w = acc_w.extend(new_w); +} + +// 5. Final decide (the only non-succinct step — wrap in a SNARK if you need succinctness). +warp.decide(acc_w, acc_x)?; +``` + +The `examples/profile_phases.rs` runs the full pipeline and prints a +per-phase wall-time breakdown when built with `--features profile`. + +## Layout + +- `src/lib.rs` — `WARP::{prove, verify, decide}` orchestrator +- `src/protocol/phases/` — IOR phases (`pesat`, `twin_constraint`, `ood`, `batching`, `proximity`) +- `src/relations/` — `R1CS`, `BundledPESAT`, `HashChainRelation` +- `src/crypto/`, `src/utils/` — Merkle wrapper, field/poly helpers +- `src/profile/` — opt-in tracing layer (gated behind the `profile` feature) +- `tests/integration_warp.rs` — end-to-end on BLS12-381 and Goldilocks +- `tests/verifier_negative.rs` — single-tamper rejection tests + +## Running tests / benches + +```sh +cargo test --release +cargo bench +cargo run --release --features profile --example profile_phases +``` From ef5d7e68759f5c3496d53d329ea5332f555b7c5d Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 21:33:29 +0200 Subject: [PATCH 38/68] =?UTF-8?q?perf:=20fuse=20(f+=CF=89=C2=B7p)=C2=B7t?= =?UTF-8?q?=20into=20direct=20accumulation=20in=20twin=5Fconstraint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The non-singleton accumulate_pair hotpath built three intermediate DensePolynomials per pair per round per constraint: q1 = f + ω·p (allocates) q2 = q1.naive_mul(t) (allocates) q1 itself was built from p * omega (allocates again) Closed form: t is degree 1 (t_0 + t_1·X), so the product (f + ω·p)·t has h_i = q_{i-1}·t_1 + q_i·t_0 with q_i = f_i + ω·p_i. Compute directly into the caller's `coeffs` buffer with no temporary polys. This drops 3 allocations per (pair, round, evaluator-call) tuple. The deeper `protogalaxy::fold` allocations producing f and p remain (they live in effsc — separate change). All 11 tests pass, confirming coefficient-identical output. Twin-constraint sumcheck is the hottest path in the prover (~55%); even a fraction of the per-pair allocator pressure removed should show up in benches. --- src/protocol/phases/twin_constraint.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/protocol/phases/twin_constraint.rs b/src/protocol/phases/twin_constraint.rs index dac33cf..dd18a97 100644 --- a/src/protocol/phases/twin_constraint.rs +++ b/src/protocol/phases/twin_constraint.rs @@ -162,14 +162,23 @@ impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { .collect(), ); - // t(X) = tau_even + (tau_odd - tau_even) · X - let t = linear_poly(tau_even, tau_odd); - - // h(X) = (f(X) + ω·p(X)) · t(X) - let h = (f + p * self.omega).naive_mul(&t); - - for (c, &hc) in coeffs.iter_mut().zip(h.coeffs.iter()) { - *c += hc; + // h(X) = (f(X) + ω·p(X)) · t(X) where t is linear t_0 + t_1·X. + // Closed form per coefficient: h_i = q_{i-1}·t_1 + q_i·t_0 with + // q_i = f_i + ω·p_i. We accumulate directly into `coeffs` so the + // (f + ω·p) sum and the (·t) multiplication never allocate temporary + // DensePolynomials — the per-pair allocation count drops by 3 + // (the +, the *omega, and the naive_mul each used to allocate). + let t0 = tau_even; + let t1 = tau_odd - tau_even; + let f_coeffs = &f.coeffs; + let p_coeffs = &p.coeffs; + let mut q_im1 = F::zero(); + for (i, c) in coeffs.iter_mut().enumerate() { + let f_i = f_coeffs.get(i).copied().unwrap_or(F::zero()); + let p_i = p_coeffs.get(i).copied().unwrap_or(F::zero()); + let q_i = f_i + self.omega * p_i; + *c += q_im1 * t1 + q_i * t0; + q_im1 = q_i; } } } From 94b49190a20775678d0340d12ece9e570ee185cc Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 21:35:21 +0200 Subject: [PATCH 39/68] sketch: typed IOR composition module (design space, not yet wired) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lays out the trait/combinator shape for replacing the orchestrator's ~250 lines of hand-threading with a typed pipeline value, e.g.: let pipeline = Pesat::new(...) .then(TwinConstraint::new(...), make_tc_statement) .then(Ood::new(), make_ood_statement) ...; Provides: - Then combinator for the linear-chain case - IORChain extension trait so any IOR gets `.then(...)` - Then::prove / Then::verify methods that demonstrate the chaining Documents the open design questions: - Linear chain vs DAG-shaped fan-in (Batching needs zeta_0 + samples_flat + queries from three upstream sources) - Witness projection: pluck-from-tuple vs WitnessProjection associated type - Why this isn't yet `impl IOR for Then<...>` — pipeline-level Statement / Witness types need projections worked out Orchestrator unchanged; this is a landing pad for incremental migration once the design questions resolve. --- src/protocol/composition.rs | 176 ++++++++++++++++++++++++++++++++++++ src/protocol/mod.rs | 1 + 2 files changed, 177 insertions(+) create mode 100644 src/protocol/composition.rs diff --git a/src/protocol/composition.rs b/src/protocol/composition.rs new file mode 100644 index 0000000..5c7c2ab --- /dev/null +++ b/src/protocol/composition.rs @@ -0,0 +1,176 @@ +//! Typed composition of [`IOR`]s — design sketch. +//! +//! # Status +//! +//! **Sketch, not yet wired.** The orchestrator in [`crate::WARP::prove`] / +//! [`crate::WARP::verify`] still threads the five phases by hand. This +//! module lays out the trait shape that would let us replace those ~250 +//! lines of bespoke threading with a typed pipeline value, and explains +//! the open design questions before we migrate. +//! +//! # Goal +//! +//! Today, swapping out (say) [`crate::protocol::phases::batching::Batching`] +//! for a different sumcheck variant requires editing the orchestrator at +//! two sites (prove + verify), updating the proof struct, and re-deriving +//! by hand which fields flow into which downstream phase. We want this to +//! be a one-line type change at the pipeline definition. +//! +//! # The two shapes WARP actually needs +//! +//! ## (a) Linear chain +//! +//! Most pairs of WARP phases compose as `A.ReducedStatement → B.Statement` +//! plus a constructor that may pull in extra orchestrator state. The +//! [`Then`] combinator below captures this case. +//! +//! ## (b) Fan-in / shared state +//! +//! The hard case: [`crate::protocol::phases::batching::BatchingStatement`] +//! is built from *three* upstream sources — the TwinConstraint reduction +//! (`zeta_0`), the Ood phase (`samples_flat`), and the orchestrator's +//! sampled query indices. A pure `A.then(B)` chain can't express that +//! directly. Two reasonable answers: +//! +//! - **Pipeline state:** thread an opaque accumulator value through the +//! chain, with each phase reading from / writing to it. Closer to a +//! typed monadic state-machine. Good ergonomics, more boilerplate per +//! phase to declare what state it touches. +//! - **Explicit assembly nodes:** make the assembly itself a typed node +//! in the pipeline (think: a "construct BatchingStatement from prior +//! outputs" node), and let the chain only handle the IORs proper. The +//! `from_phase_outputs` constructor on `BatchingStatement` (added in +//! the IOR-cleanup pass) is already the right shape for this — we'd +//! lift it to a trait method. +//! +//! The second is closer to the "single source of truth" pattern that +//! [`IOR::reduce_statement`] already enforces inside each phase, and +//! plays well with the orchestrator owning between-phase computations +//! (e.g. the `eta` / `nu_0` reads, the `td_new` commit, the query +//! sampling). The pipeline holds the phases; the orchestrator owns the +//! glue. +//! +//! # Sketch +//! +//! ```ignore +//! let pipeline = Pesat::new(...) +//! .then(TwinConstraint::new(...)) +//! .then(Ood::new()) +//! .then(Batching::new()) +//! .then(Proximity::new(...)); +//! +//! let (final_reduced, proof) = pipeline.prove(&mut prover_state, statement, witness)?; +//! ``` +//! +//! Open: how `Witness` flows. WARP phases mostly take witness slices by +//! reference, with each phase reading a different subset. A pipeline +//! value either has to (a) accept a single tuple-of-all-witnesses and +//! pluck per phase, or (b) take the witness once and re-borrow into +//! each phase. (b) is closer to the current orchestrator and avoids +//! re-cloning, but requires a `WitnessProjection

    ` associated type on +//! each phase. + +use spongefish::{ProverState, VerifierState}; + +use crate::error::{ProverError, VerifierError}; +use crate::protocol::phases::IOR; + +/// Two-phase linear composition. The next phase's statement is built +/// from the previous phase's [`IOR::ReducedStatement`] via `make_next`. +/// +/// **Limitation:** captures only "B's statement is fully determined by +/// A's reduced statement" — does not handle WARP's fan-in case where +/// downstream phases need state from multiple upstream phases plus the +/// orchestrator. See module docs. +pub struct Then { + pub first: A, + pub second: B, + pub make_next: MakeNext, +} + +impl Then { + pub fn new(first: A, second: B, make_next: MakeNext) -> Self { + Self { + first, + second, + make_next, + } + } +} + +/// Extension trait so any [`IOR`] can be chained with `.then(next, make_next)`. +pub trait IORChain: IOR + Sized { + fn then(self, next: B, make_next: MakeNext) -> Then + where + B: IOR, + MakeNext: Fn(&Self::ReducedStatement) -> B::Statement, + { + Then::new(self, next, make_next) + } +} + +impl IORChain for T {} + +/// Composed prove: run `first.prove`, derive `second`'s statement via +/// `make_next`, run `second.prove`. Returns `second`'s reduced statement +/// and a tuple of both proof strings. +/// +/// **Why this isn't yet a `impl IOR for Then<...>`**: the composition's +/// associated types (`ReducedStatement`, `ProofString`, etc.) don't +/// align trivially with the [`IOR`] trait — the proof string becomes a +/// tuple, the reduced witness becomes the second's, and pipeline-level +/// `Statement` / `Witness` need projections. Working out those projections +/// is the next design task; the prove/verify methods below are the +/// minimum viable demonstration. +impl Then +where + A: IOR, + B: IOR, + MakeNext: Fn(&A::ReducedStatement) -> B::Statement, +{ + #[allow(clippy::type_complexity)] + pub fn prove( + &self, + prover_state: &mut ProverState, + a_statement: &A::Statement, + a_witness: &A::Witness, + a_inputs: &A::ProverInputs, + b_witness: &B::Witness, + b_inputs: &B::ProverInputs, + ) -> Result< + ( + B::ReducedStatement, + (A::ProofString, B::ProofString), + B::ReducedWitness, + ), + ProverError, + > { + let (a_reduced, a_proof, _a_red_wit) = + self.first.prove(prover_state, a_statement, a_witness, a_inputs)?; + let b_statement = (self.make_next)(&a_reduced); + let (b_reduced, b_proof, b_red_wit) = + self.second + .prove(prover_state, &b_statement, b_witness, b_inputs)?; + Ok((b_reduced, (a_proof, b_proof), b_red_wit)) + } + + #[allow(clippy::type_complexity)] + pub fn verify<'a>( + &self, + verifier_state: &mut VerifierState<'a>, + a_statement: &A::Statement, + a_inputs: &A::VerifierInputs, + b_inputs: &B::VerifierInputs, + ) -> Result< + ( + B::ReducedStatement, + (A::VerifierOutputs, B::VerifierOutputs), + ), + VerifierError, + > { + let (a_reduced, a_vouts) = self.first.verify(verifier_state, a_statement, a_inputs)?; + let b_statement = (self.make_next)(&a_reduced); + let (b_reduced, b_vouts) = self.second.verify(verifier_state, &b_statement, b_inputs)?; + Ok((b_reduced, (a_vouts, b_vouts))) + } +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 55ed4d7..25388ea 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,3 +1,4 @@ +pub mod composition; pub mod oracle; pub mod phases; pub mod query; From 81734de71a0110c417fc990e1676b8c633296933 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 21:50:55 +0200 Subject: [PATCH 40/68] sketch: Pipeline trait above IOR, with WARP composition example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reframes the design: keep IOR as the single-phase abstraction it already is (well-shaped — reduce_statement is a real soundness win), build a new Pipeline trait *above* it for composition. Writes up the case for not extending IOR itself: ReductionInputs doesn't generalize, nested-tuple Proof shapes don't help, and WARP's data flow is DAG-shaped (Batching reads from three upstream sources), which IOR has no place for. Pipeline trait sketch with five associated types: Statement, Witness, Context (orchestrator-owned shared state, populated mid-pipeline), Proof (flat — pipeline owns assembly), FinalReduced. Three combinator types declared but bodies deferred: - Lift turns one IOR into a one-phase Pipeline - Then chains two Pipelines with an inter-phase context update hook (where the WARP orchestrator's td_new commit / zetas_prefix assembly / query sampling would land) - WithFlatProof closes the "no nested tuples leak" promise at the leaf Worked example walks through how WARP's five phases would map onto this — what the Context contains, what each phase's lift looks like, where the assembly function names the global proof shape. Open design questions called out: Context mutability, ProverInputs lifetime story, verifier-side oracle handles. Trait impls deferred until those resolve to avoid baking premature decisions into the public surface. --- src/protocol/composition.rs | 362 +++++++++++++++++++++--------------- 1 file changed, 213 insertions(+), 149 deletions(-) diff --git a/src/protocol/composition.rs b/src/protocol/composition.rs index 5c7c2ab..e47b34c 100644 --- a/src/protocol/composition.rs +++ b/src/protocol/composition.rs @@ -4,173 +4,237 @@ //! //! **Sketch, not yet wired.** The orchestrator in [`crate::WARP::prove`] / //! [`crate::WARP::verify`] still threads the five phases by hand. This -//! module lays out the trait shape that would let us replace those ~250 -//! lines of bespoke threading with a typed pipeline value, and explains -//! the open design questions before we migrate. -//! -//! # Goal -//! -//! Today, swapping out (say) [`crate::protocol::phases::batching::Batching`] -//! for a different sumcheck variant requires editing the orchestrator at -//! two sites (prove + verify), updating the proof struct, and re-deriving -//! by hand which fields flow into which downstream phase. We want this to -//! be a one-line type change at the pipeline definition. -//! -//! # The two shapes WARP actually needs -//! -//! ## (a) Linear chain -//! -//! Most pairs of WARP phases compose as `A.ReducedStatement → B.Statement` -//! plus a constructor that may pull in extra orchestrator state. The -//! [`Then`] combinator below captures this case. -//! -//! ## (b) Fan-in / shared state -//! -//! The hard case: [`crate::protocol::phases::batching::BatchingStatement`] -//! is built from *three* upstream sources — the TwinConstraint reduction -//! (`zeta_0`), the Ood phase (`samples_flat`), and the orchestrator's -//! sampled query indices. A pure `A.then(B)` chain can't express that -//! directly. Two reasonable answers: -//! -//! - **Pipeline state:** thread an opaque accumulator value through the -//! chain, with each phase reading from / writing to it. Closer to a -//! typed monadic state-machine. Good ergonomics, more boilerplate per -//! phase to declare what state it touches. -//! - **Explicit assembly nodes:** make the assembly itself a typed node -//! in the pipeline (think: a "construct BatchingStatement from prior -//! outputs" node), and let the chain only handle the IORs proper. The -//! `from_phase_outputs` constructor on `BatchingStatement` (added in -//! the IOR-cleanup pass) is already the right shape for this — we'd -//! lift it to a trait method. -//! -//! The second is closer to the "single source of truth" pattern that -//! [`IOR::reduce_statement`] already enforces inside each phase, and -//! plays well with the orchestrator owning between-phase computations -//! (e.g. the `eta` / `nu_0` reads, the `td_new` commit, the query -//! sampling). The pipeline holds the phases; the orchestrator owns the -//! glue. -//! -//! # Sketch +//! module proposes a layered design — keep [`IOR`] as the single-phase +//! abstraction it already is, build a new [`Pipeline`] trait above it +//! for composition. +//! +//! # Why two traits, not one extended one +//! +//! `IOR` is well-shaped for one phase: `(stmt, wit, oracles_in) → +//! (reduced_stmt, oracles_out)` plus the [`IOR::reduce_statement`] single +//! source of truth. Trying to make a `Then` value `impl IOR` runs +//! into three problems: +//! +//! - `ReductionInputs` doesn't generalize. It's per-phase by construction +//! — a composition reduces a *sequence* of statements, not one. +//! - `ProofString = (A::ProofString, B::ProofString)` nests 5-deep for +//! WARP. Extracting pieces to assemble the global proof is just the +//! hand-threading we wanted to eliminate, in a different shape. +//! - WARP's data flow isn't a clean linear chain — it's DAG-shaped. +//! `BatchingStatement` reads from TwinConstraint's reduction *and* +//! Ood's outputs *and* orchestrator-sampled queries. `IOR` has no +//! place for "between-phase orchestrator work." +//! +//! Analogy: `Functor` captures `fmap`, `Monad` captures sequencing — +//! they're separate traits because they're different concepts. Same here: +//! `IOR` captures phase semantics; `Pipeline` captures composition. +//! Folding sequencing into `IOR` makes both worse. +//! +//! # The shape //! //! ```ignore -//! let pipeline = Pesat::new(...) -//! .then(TwinConstraint::new(...)) -//! .then(Ood::new()) -//! .then(Batching::new()) -//! .then(Proximity::new(...)); +//! trait Pipeline { +//! type Statement; +//! type Witness; +//! type Context; // orchestrator-owned state read by phases +//! type Proof; // flat — pipeline owns the assembly +//! type FinalReduced; // the last phase's ReducedStatement +//! fn prove(...) -> Result<(Self::FinalReduced, Self::Proof), ProverError>; +//! fn verify(...) -> Result; +//! } +//! ``` +//! +//! Three things make this work where the bare `Then.impl IOR` attempt +//! didn't: +//! +//! 1. **Context as a first-class associated type.** WARP's actual data +//! flow has each phase reading some upstream-phase output *and* some +//! pipeline-level shared state (the `acc_instance`, the fresh +//! witnesses, the sampled queries, etc.). Context is that shared +//! state. The orchestrator builds it once; each phase reads what it +//! needs. +//! 2. **Witness projection.** Each phase reads a different slice of the +//! pipeline's `Witness`. A composition combinator takes a +//! user-supplied projection `Self::Witness → SubPhase::Witness` so +//! no re-cloning is forced. +//! 3. **Flat `Proof`.** The pipeline owns proof assembly — a leaf +//! pipeline produces a concrete user-defined proof type via a +//! user-supplied `assemble` function. No nested-tuple +//! `(((A, B), C), D)` shapes leak out. +//! +//! # Worked example: how WARP would compose //! -//! let (final_reduced, proof) = pipeline.prove(&mut prover_state, statement, witness)?; +//! ```ignore +//! // Context = everything the orchestrator computes / holds across phases. +//! struct WARPContext<'a, F, H> { +//! acc_instance: &'a AccumulatorInstance, +//! acc_witness: &'a AccumulatorWitness, +//! acc_codewords: &'a [Vec], +//! instances: &'a [Vec], +//! witnesses: &'a [Vec], +//! queries: Option>, // populated mid-pipeline +//! pesat_outputs: Option>, // populated by Pesat phase +//! // ... etc. +//! } +//! +//! let pipeline = +//! Pesat::new(...) +//! .lift(/* prover_inputs_fn */ |_, ctx| (), /* witness_fn */ |w, _| w.fresh) +//! .then( +//! TwinConstraint::new(...).lift(...), +//! /* make_next_stmt */ |pesat_red, ctx| TwinConstraintStatement { +//! acc_instance: ctx.acc_instance.clone(), +//! l1_mus: pesat_red.mus.clone(), +//! l1_taus: pesat_red.taus.clone(), +//! .. +//! }, +//! ) +//! .then(Ood::new().lift(...), /* make_next_stmt */ |_, _| OodStatement { .. }) +//! .then(Batching::new().lift(...), |_, ctx| BatchingStatement::from_phase_outputs(...)) +//! .then(Proximity::new(...).lift(...), |_, ctx| ProximityStatement { .. }) +//! .assemble(|reduced, parts, ctx| WARPProof { +//! rt_0: parts.pesat.td_0_root, +//! mu_i: parts.pesat.mus, +//! // ... pluck from named parts, not nested tuples +//! }); //! ``` //! -//! Open: how `Witness` flows. WARP phases mostly take witness slices by -//! reference, with each phase reading a different subset. A pipeline -//! value either has to (a) accept a single tuple-of-all-witnesses and -//! pluck per phase, or (b) take the witness once and re-borrow into -//! each phase. (b) is closer to the current orchestrator and avoids -//! re-cloning, but requires a `WitnessProjection

    ` associated type on -//! each phase. +//! Swapping `Batching` for a different sumcheck variant becomes a one-line +//! type change at the pipeline definition. The phases stay +//! self-contained; the assembly function is the only place that names +//! the global proof shape. +//! +//! # Open design questions +//! +//! - **Is `Context` `&mut`?** Some phases mutate (e.g., the orchestrator +//! sampling query indices "between" Ood and Batching). A `&mut Context` +//! threaded through `prove` is one answer; an explicit "context node" +//! in the pipeline (no IOR call, just a context update) is another. +//! - **`ProverInputs` lifetime story.** Phases like +//! [`crate::protocol::phases::twin_constraint::TwinConstraintProverInputs`] +//! borrow from upstream reduced witnesses. The pipeline value can't +//! live longer than those borrows. Either (a) `prove` takes a +//! `WitnessRef` instead of `Witness`, or (b) the lifetime is on +//! `Pipeline` itself. +//! - **Verifier-side oracle handles.** Verifier `ProverInputs` carry +//! `IndexedOracle` / `EvalOracle` handles — exactly the refactor +//! already noted in +//! `[crate::protocol::phases::oracle_handle]`. Pipeline composition +//! slots in *over* that abstraction; we shouldn't conflate the two. use spongefish::{ProverState, VerifierState}; use crate::error::{ProverError, VerifierError}; use crate::protocol::phases::IOR; -/// Two-phase linear composition. The next phase's statement is built -/// from the previous phase's [`IOR::ReducedStatement`] via `make_next`. +/// Composition of one or more [`IOR`]s into a typed pipeline. /// -/// **Limitation:** captures only "B's statement is fully determined by -/// A's reduced statement" — does not handle WARP's fan-in case where -/// downstream phases need state from multiple upstream phases plus the -/// orchestrator. See module docs. -pub struct Then { - pub first: A, - pub second: B, - pub make_next: MakeNext, -} +/// Models WARP-shaped flows: each phase reads from its own statement +/// plus a shared `Context`, and emits a chunk of proof bytes plus its +/// reduced statement. The pipeline value owns proof assembly: callers +/// see one flat [`Pipeline::Proof`] type and one flat +/// [`Pipeline::FinalReduced`] result, not nested tuples. +/// +/// **Not yet wired.** The methods below are the minimum-viable shape; +/// no current WARP code calls them. See module docs for the worked +/// example of how the five phases would compose. +pub trait Pipeline { + /// Top-level statement supplied to the pipeline. Each phase derives + /// its own statement from this plus the upstream reduction. + type Statement; + /// Top-level witness. Each phase projects out the slice it needs. + type Witness; + /// Orchestrator-owned shared state read by phases. WARP's + /// `acc_instance`, fresh witnesses, sampled queries, and inter-phase + /// derived values live here. + type Context; + /// Flat global proof value. Pipeline owns assembly. + type Proof; + /// The last phase's [`IOR::ReducedStatement`]. + type FinalReduced; -impl Then { - pub fn new(first: A, second: B, make_next: MakeNext) -> Self { - Self { - first, - second, - make_next, - } - } + fn prove( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + witness: &Self::Witness, + context: &mut Self::Context, + ) -> Result<(Self::FinalReduced, Self::Proof), ProverError>; + + fn verify<'a>( + &self, + verifier_state: &mut VerifierState<'a>, + statement: &Self::Statement, + context: &mut Self::Context, + proof: &Self::Proof, + ) -> Result; } -/// Extension trait so any [`IOR`] can be chained with `.then(next, make_next)`. -pub trait IORChain: IOR + Sized { - fn then(self, next: B, make_next: MakeNext) -> Then - where - B: IOR, - MakeNext: Fn(&Self::ReducedStatement) -> B::Statement, - { - Then::new(self, next, make_next) - } +/// Lift a single [`IOR`] into a one-phase [`Pipeline`]. +/// +/// The user-supplied closures bridge [`IOR`]'s per-phase types +/// (`Statement`, `Witness`, `ProverInputs`, `VerifierInputs`) to the +/// pipeline-level types — they're how a phase declares "here's how I +/// project myself out of the pipeline-level state." +/// +/// **Sketch only.** Concrete implementation deferred until the lifetime +/// design (open question #2 in module docs) is settled — the closures +/// likely need to return references rather than owned values for +/// production use. +pub struct Lift { + pub inner: I, + pub make_statement: MakeStmt, + pub make_witness: MakeWit, + pub make_prover_inputs: MakeProverInputs, + pub make_verifier_inputs: MakeVerifierInputs, + pub assemble_proof: AssembleProof, } -impl IORChain for T {} +/// Sequential composition of two pipelines. +/// +/// `make_next_context` runs *between* the two phases' `prove`/`verify` +/// calls. It's where orchestrator-owned glue lives — sampling queries, +/// reading `eta`/`nu_0` off the transcript, committing to a new oracle. +/// In the WARP case, the `td_new` commit and the OOD-to-Batching +/// `zetas_prefix` assembly would land here. +pub struct Then { + pub first: A, + pub second: B, + pub make_next_context: MakeNextContext, +} -/// Composed prove: run `first.prove`, derive `second`'s statement via -/// `make_next`, run `second.prove`. Returns `second`'s reduced statement -/// and a tuple of both proof strings. +/// Convert a pipeline's nested intermediate values into a flat +/// user-defined [`Pipeline::Proof`]. /// -/// **Why this isn't yet a `impl IOR for Then<...>`**: the composition's -/// associated types (`ReducedStatement`, `ProofString`, etc.) don't -/// align trivially with the [`IOR`] trait — the proof string becomes a -/// tuple, the reduced witness becomes the second's, and pipeline-level -/// `Statement` / `Witness` need projections. Working out those projections -/// is the next design task; the prove/verify methods below are the -/// minimum viable demonstration. -impl Then -where - A: IOR, - B: IOR, - MakeNext: Fn(&A::ReducedStatement) -> B::Statement, -{ - #[allow(clippy::type_complexity)] - pub fn prove( - &self, - prover_state: &mut ProverState, - a_statement: &A::Statement, - a_witness: &A::Witness, - a_inputs: &A::ProverInputs, - b_witness: &B::Witness, - b_inputs: &B::ProverInputs, - ) -> Result< - ( - B::ReducedStatement, - (A::ProofString, B::ProofString), - B::ReducedWitness, - ), - ProverError, - > { - let (a_reduced, a_proof, _a_red_wit) = - self.first.prove(prover_state, a_statement, a_witness, a_inputs)?; - let b_statement = (self.make_next)(&a_reduced); - let (b_reduced, b_proof, b_red_wit) = - self.second - .prove(prover_state, &b_statement, b_witness, b_inputs)?; - Ok((b_reduced, (a_proof, b_proof), b_red_wit)) - } +/// Wraps any `Pipeline` and replaces its `Proof` type. The `Map` closure +/// receives the inner proof + final reduced statement and returns the +/// caller's flat proof shape. This is the leaf that closes the +/// "no nested tuples leak out" property promised in the trait docs. +pub struct WithFlatProof { + pub inner: P, + pub map: Map, + pub _phantom: std::marker::PhantomData, +} - #[allow(clippy::type_complexity)] - pub fn verify<'a>( - &self, - verifier_state: &mut VerifierState<'a>, - a_statement: &A::Statement, - a_inputs: &A::VerifierInputs, - b_inputs: &B::VerifierInputs, - ) -> Result< - ( - B::ReducedStatement, - (A::VerifierOutputs, B::VerifierOutputs), - ), - VerifierError, - > { - let (a_reduced, a_vouts) = self.first.verify(verifier_state, a_statement, a_inputs)?; - let b_statement = (self.make_next)(&a_reduced); - let (b_reduced, b_vouts) = self.second.verify(verifier_state, &b_statement, b_inputs)?; - Ok((b_reduced, (a_vouts, b_vouts))) - } +// Trait bounds and impls deliberately omitted from this sketch. +// +// Writing `impl<...> Pipeline for Then<...>` requires resolving the +// `Context` + lifetime questions in the module docs first; doing it +// prematurely would bake those decisions into the public surface and +// force a churn-y revision later. Treat this file as a design landing +// pad: extend the types and add impls once a concrete migration target +// is picked (probably starting with the Pesat → TwinConstraint pair, +// the smallest WARP-internal composition that exercises Context-passing +// and `make_next_statement` together). + +// Suppress warnings about the unused IOR / spongefish imports — they +// document the design dependencies even though no code uses them yet. +#[allow(dead_code)] +fn _design_deps_marker( + _: &mut ProverState, + _: &mut VerifierState<'_>, + _: ProverError, + _: VerifierError, + _: &I, +) { } From 170161726a7777153622fdbbe54058ee32276a1b Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 22:07:51 +0200 Subject: [PATCH 41/68] ior: GAT-ify Witness/ProverInputs/VerifierInputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Decouples inter-phase data lifetime from the phase struct's lifetime so a Pipeline value can hold phases long-term and feed them short-lived inputs. Without this, the orchestrator's `'a` unified phase-struct and ProverInputs lifetimes — fine for the function-local pattern, but blocks typed composition. Trait changes (src/protocol/phases/mod.rs): - type Witness -> type Witness<'a> where Self: 'a - type ProverInputs -> type ProverInputs<'a> where Self: 'a - type VerifierInputs -> type VerifierInputs<'a> where Self: 'a - prove_inner / prove gain `<'a>` + `where Self: 'a` - verify_inner / verify gain `<'a, 'b>` + `where Self: 'b` Each phase impl gets: - GAT impls with matching `where Self: 'b` bounds - inner-method bounds `'phase: 'inputs` (and `H: 'inputs` for phases generic over the hasher) so the phase outlives whatever borrows the inputs hold Net behavior unchanged — orchestrator still calls phase.prove(...) with function-local statement/witness/inputs and Rust infers the new lifetime to the same scope. All 11 tests pass; coefficient-identical output. Pipeline composition (src/protocol/composition.rs) can now express "phase A's reduced witness lives across the call to phase B" without forcing the phase structs to live for the borrow's duration. --- src/protocol/phases/batching.rs | 35 +++++++++++----- src/protocol/phases/mod.rs | 57 +++++++++++++++++--------- src/protocol/phases/ood.rs | 35 +++++++++++----- src/protocol/phases/pesat.rs | 37 ++++++++++++----- src/protocol/phases/proximity.rs | 45 +++++++++++++------- src/protocol/phases/twin_constraint.rs | 37 ++++++++++++----- 6 files changed, 173 insertions(+), 73 deletions(-) diff --git a/src/protocol/phases/batching.rs b/src/protocol/phases/batching.rs index 0bc01b8..ac44815 100644 --- a/src/protocol/phases/batching.rs +++ b/src/protocol/phases/batching.rs @@ -178,9 +178,18 @@ where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { type Statement = BatchingStatement; - type Witness = (); - type ProverInputs = BatchingProverInputs<'a, F>; - type VerifierInputs = BatchingVerifierInputs; + type Witness<'b> + = () + where + Self: 'b; + type ProverInputs<'b> + = BatchingProverInputs<'b, F> + where + Self: 'b; + type VerifierInputs<'b> + = BatchingVerifierInputs + where + Self: 'b; type ReductionInputs = BatchingReductionInputs; type ReducedStatement = BatchingReducedStatement; type ProofString = (); @@ -202,12 +211,12 @@ where skip_all, fields(s = statement.s, t = statement.t, log_n = statement.log_n) )] - fn prove_inner( + fn prove_inner<'b>( &self, prover_state: &mut ProverState, statement: &Self::Statement, - _witness: &Self::Witness, - inputs: &Self::ProverInputs, + _witness: &Self::Witness<'b>, + inputs: &Self::ProverInputs<'b>, ) -> Result< ( Self::ReductionInputs, @@ -215,7 +224,10 @@ where Self::ReducedWitness, ), ProverError, - > { + > + where + 'a: 'b, + { let n = inputs.oracle.len(); let r = 1 + statement.s + statement.t; let log_r = log2(r) as usize; @@ -272,12 +284,15 @@ where skip_all, fields(s = statement.s, t = statement.t, log_n = statement.log_n) )] - fn verify_inner<'b>( + fn verify_inner<'b, 'c>( &self, verifier_state: &mut VerifierState<'b>, statement: &Self::Statement, - inputs: &Self::VerifierInputs, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> { + inputs: &Self::VerifierInputs<'c>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + where + 'a: 'c, + { let r = 1 + statement.s + statement.t; let log_r = log2(r) as usize; debug_assert_eq!(statement.zetas_prefix.len(), r); diff --git a/src/protocol/phases/mod.rs b/src/protocol/phases/mod.rs index 4872b46..0e79758 100644 --- a/src/protocol/phases/mod.rs +++ b/src/protocol/phases/mod.rs @@ -60,13 +60,22 @@ use crate::error::{ProverError, VerifierError}; pub trait IOR { /// Pre-reduction claim. Visible to prover and verifier. type Statement; - /// Prover-only data the prover reads (typically a reference-holding - /// wrapper — see the convention note on the trait). - type Witness; + /// Prover-only data the prover reads. Lifetime-parameterized so a + /// pipeline composition can borrow inter-phase data into the witness + /// for one `prove` call without the borrow leaking into the phase + /// struct's lifetime — see `crate::protocol::composition`. + type Witness<'a> + where + Self: 'a; /// Oracles flowing in from upstream IORs (prover view: full data). - type ProverInputs; + /// Lifetime-parameterized for the same reason as `Witness`. + type ProverInputs<'a> + where + Self: 'a; /// Oracles flowing in from upstream IORs (verifier view: commitments). - type VerifierInputs; + type VerifierInputs<'a> + where + Self: 'a; /// Explicit inputs that determine [`Self::ReducedStatement`]. Both /// prover-side and verifier-side machinery produce this struct, then /// hand it to [`Self::reduce_statement`] for the (single) statement @@ -103,12 +112,12 @@ pub trait IOR { /// Implementor's prover-side body. Runs the protocol's prover /// machinery and returns the inputs `reduce_statement` needs, plus /// the proof string and reduced witness. - fn prove_inner( + fn prove_inner<'a>( &self, prover_state: &mut ProverState, statement: &Self::Statement, - witness: &Self::Witness, - inputs: &Self::ProverInputs, + witness: &Self::Witness<'a>, + inputs: &Self::ProverInputs<'a>, ) -> Result< ( Self::ReductionInputs, @@ -116,25 +125,29 @@ pub trait IOR { Self::ReducedWitness, ), ProverError, - >; + > + where + Self: 'a; /// Implementor's verifier-side body. Runs the protocol's verifier /// machinery (sumcheck checks, transcript reads, soundness checks) /// and returns the inputs `reduce_statement` needs. - fn verify_inner<'a>( + fn verify_inner<'a, 'b>( &self, verifier_state: &mut VerifierState<'a>, statement: &Self::Statement, - inputs: &Self::VerifierInputs, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError>; + inputs: &Self::VerifierInputs<'b>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + where + Self: 'b; /// Default impl. Implementors should not override. - fn prove( + fn prove<'a>( &self, prover_state: &mut ProverState, statement: &Self::Statement, - witness: &Self::Witness, - inputs: &Self::ProverInputs, + witness: &Self::Witness<'a>, + inputs: &Self::ProverInputs<'a>, ) -> Result< ( Self::ReducedStatement, @@ -142,7 +155,10 @@ pub trait IOR { Self::ReducedWitness, ), ProverError, - > { + > + where + Self: 'a, + { let (red_inputs, proof, red_wit) = self.prove_inner(prover_state, statement, witness, inputs)?; let reduced = self.reduce_statement(statement, &red_inputs); @@ -150,12 +166,15 @@ pub trait IOR { } /// Default impl. Implementors should not override. - fn verify<'a>( + fn verify<'a, 'b>( &self, verifier_state: &mut VerifierState<'a>, statement: &Self::Statement, - inputs: &Self::VerifierInputs, - ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> { + inputs: &Self::VerifierInputs<'b>, + ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> + where + Self: 'b, + { let (red_inputs, vouts) = self.verify_inner(verifier_state, statement, inputs)?; let reduced = self.reduce_statement(statement, &red_inputs); Ok((reduced, vouts)) diff --git a/src/protocol/phases/ood.rs b/src/protocol/phases/ood.rs index 150027a..a799357 100644 --- a/src/protocol/phases/ood.rs +++ b/src/protocol/phases/ood.rs @@ -74,9 +74,18 @@ where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { type Statement = OodStatement; - type Witness = (); - type ProverInputs = OodProverInputs<'a, F>; - type VerifierInputs = (); + type Witness<'b> + = () + where + Self: 'b; + type ProverInputs<'b> + = OodProverInputs<'b, F> + where + Self: 'b; + type VerifierInputs<'b> + = () + where + Self: 'b; type ReductionInputs = OodReductionInputs; type ReducedStatement = OodReducedStatement; type ProofString = (); @@ -95,12 +104,12 @@ where } #[tracing::instrument(name = "ood", skip_all, fields(s = statement.s, log_n = statement.log_n))] - fn prove_inner( + fn prove_inner<'b>( &self, prover_state: &mut ProverState, statement: &Self::Statement, - _witness: &Self::Witness, - inputs: &Self::ProverInputs, + _witness: &Self::Witness<'b>, + inputs: &Self::ProverInputs<'b>, ) -> Result< ( Self::ReductionInputs, @@ -108,7 +117,10 @@ where Self::ReducedWitness, ), ProverError, - > { + > + where + 'a: 'b, + { let samples_flat = prover_state.verifier_messages_vec::(statement.s * statement.log_n); count_ops!(OodPointQueries, statement.s as u64); let answers = samples_flat @@ -131,12 +143,15 @@ where skip_all, fields(s = statement.s, log_n = statement.log_n) )] - fn verify_inner<'b>( + fn verify_inner<'b, 'c>( &self, verifier_state: &mut VerifierState<'b>, statement: &Self::Statement, - _inputs: &Self::VerifierInputs, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> { + _inputs: &Self::VerifierInputs<'c>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + where + 'a: 'c, + { let samples_flat: Vec = (0..statement.s * statement.log_n) .map(|_| verifier_state.verifier_message::()) .collect(); diff --git a/src/protocol/phases/pesat.rs b/src/protocol/phases/pesat.rs index 9624db9..8504832 100644 --- a/src/protocol/phases/pesat.rs +++ b/src/protocol/phases/pesat.rs @@ -80,9 +80,18 @@ where H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { type Statement = PesatStatement; - type Witness = PesatWitness<'a, F>; - type ProverInputs = (); - type VerifierInputs = (); + type Witness<'b> + = PesatWitness<'b, F> + where + Self: 'b; + type ProverInputs<'b> + = () + where + Self: 'b; + type VerifierInputs<'b> + = () + where + Self: 'b; type ReductionInputs = PesatReductionInputs; type ReducedStatement = PesatReducedStatement; type ProofString = (); @@ -105,12 +114,12 @@ where skip_all, fields(l1 = statement.l1, log_m = statement.log_m, n_witnesses = witness.witnesses.len()) )] - fn prove_inner( + fn prove_inner<'b>( &self, prover_state: &mut ProverState, statement: &Self::Statement, - witness: &Self::Witness, - _inputs: &Self::ProverInputs, + witness: &Self::Witness<'b>, + _inputs: &Self::ProverInputs<'b>, ) -> Result< ( Self::ReductionInputs, @@ -118,7 +127,11 @@ where Self::ReducedWitness, ), ProverError, - > { + > + where + 'a: 'b, + H: 'b, + { // a. encode witnesses let codewords = { let _s = tracing::info_span!("pesat.encode").entered(); @@ -163,12 +176,16 @@ where skip_all, fields(l1 = statement.l1, log_m = statement.log_m) )] - fn verify_inner<'b>( + fn verify_inner<'b, 'c>( &self, verifier_state: &mut VerifierState<'b>, statement: &Self::Statement, - _inputs: &Self::VerifierInputs, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> { + _inputs: &Self::VerifierInputs<'c>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + where + 'a: 'c, + H: 'c, + { // commitment digest let rt_0: H::Digest = verifier_state.prover_message()?; diff --git a/src/protocol/phases/proximity.rs b/src/protocol/phases/proximity.rs index 4e552e8..520d99a 100644 --- a/src/protocol/phases/proximity.rs +++ b/src/protocol/phases/proximity.rs @@ -102,13 +102,22 @@ where H: MerkleHasher>, { type Statement = ProximityStatement; - type Witness = (); - type ProverInputs = ProximityProverInputs<'a, F, H>; - type VerifierInputs = ProximityVerifierInputs< - 'a, - F, - crate::protocol::phases::oracle_handle::MerkleIndexedOracle<'a, F, H>, - >; + type Witness<'b> + = () + where + Self: 'b; + type ProverInputs<'b> + = ProximityProverInputs<'b, F, H> + where + Self: 'b; + type VerifierInputs<'b> + = ProximityVerifierInputs< + 'b, + F, + crate::protocol::phases::oracle_handle::MerkleIndexedOracle<'b, F, H>, + > + where + Self: 'b; type ReductionInputs = (); type ReducedStatement = (); type ProofString = ProximityProofString; @@ -130,12 +139,12 @@ where n_accumulators = inputs.acc_td.len(), ) )] - fn prove_inner( + fn prove_inner<'b>( &self, _prover_state: &mut ProverState, statement: &Self::Statement, - _witness: &Self::Witness, - inputs: &Self::ProverInputs, + _witness: &Self::Witness<'b>, + inputs: &Self::ProverInputs<'b>, ) -> Result< ( Self::ReductionInputs, @@ -143,7 +152,11 @@ where Self::ReducedWitness, ), ProverError, - > { + > + where + 'a: 'b, + H: 'b, + { let leaf_positions = &statement.queries.leaf_positions; // ark-mt's `open()` requires strictly-sorted, unique indices. Query @@ -218,12 +231,16 @@ where skip_all, fields(t = statement.t, l2 = statement.l2) )] - fn verify_inner<'b>( + fn verify_inner<'b, 'c>( &self, _verifier_state: &mut VerifierState<'b>, statement: &Self::Statement, - inputs: &Self::VerifierInputs, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> { + inputs: &Self::VerifierInputs<'c>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + where + 'a: 'c, + H: 'c, + { // Arity / shape checks (orchestrator-side concerns the phase still // owns: number of accumulator openings must match l2; number of // shift queries is implicitly checked by the handle constructors). diff --git a/src/protocol/phases/twin_constraint.rs b/src/protocol/phases/twin_constraint.rs index dd18a97..a0f8034 100644 --- a/src/protocol/phases/twin_constraint.rs +++ b/src/protocol/phases/twin_constraint.rs @@ -290,9 +290,18 @@ where H: MerkleHasher, { type Statement = TwinConstraintStatement; - type Witness = TwinConstraintWitness<'a, F>; - type ProverInputs = TwinConstraintProverInputs<'a, F>; - type VerifierInputs = (); + type Witness<'b> + = TwinConstraintWitness<'b, F> + where + Self: 'b; + type ProverInputs<'b> + = TwinConstraintProverInputs<'b, F> + where + Self: 'b; + type VerifierInputs<'b> + = () + where + Self: 'b; type ReductionInputs = TwinConstraintReductionInputs; type ReducedStatement = TwinConstraintReducedStatement; type ProofString = (); @@ -348,12 +357,12 @@ where skip_all, fields(log_l = statement.log_l, log_m = statement.log_m, log_n = statement.log_n) )] - fn prove_inner( + fn prove_inner<'b>( &self, prover_state: &mut ProverState, statement: &Self::Statement, - witness: &Self::Witness, - inputs: &Self::ProverInputs, + witness: &Self::Witness<'b>, + inputs: &Self::ProverInputs<'b>, ) -> Result< ( Self::ReductionInputs, @@ -361,7 +370,11 @@ where Self::ReducedWitness, ), ProverError, - > { + > + where + 'a: 'b, + H: 'b, + { let l1 = inputs.fresh_codewords.len(); let log_l = statement.log_l; let log_m = statement.log_m; @@ -454,12 +467,16 @@ where skip_all, fields(log_l = statement.log_l, log_m = statement.log_m, log_n = statement.log_n) )] - fn verify_inner<'b>( + fn verify_inner<'b, 'c>( &self, verifier_state: &mut VerifierState<'b>, statement: &Self::Statement, - _inputs: &Self::VerifierInputs, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> { + _inputs: &Self::VerifierInputs<'c>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + where + 'a: 'c, + H: 'c, + { let log_l = statement.log_l; let log_n = statement.log_n; let l1 = statement.l1_mus.len(); From 90dac4a1fa6a43d3c6e44a0135bf642dc5c57f74 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 22:44:40 +0200 Subject: [PATCH 42/68] composition: PesatTwinConstraint pipeline + demo test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First concrete composition built on the GAT-decoupled IOR. Validates the design end-to-end by holding both phases (Pesat + TwinConstraint) in one Pipeline value with 'phase tied to long-lived params, then calling prove() twice in the same scope with different short-lived witness/instance batches. The second prove() call wouldn't compile pre-GAT-migration: the first call's witness borrows would have been pinned to the pipeline's 'phase, locking out a second batch with independent lifetime. With the GAT'd IOR::Witness<'a> / ProverInputs<'a>, each prove() picks its own short 'a — exactly the decoupling we wanted. Pipeline shape: - PesatTwinConstraint::new(code, hasher, r1cs) — borrows the long-lived params once - prove(prover_state, &config, PesatTcInputs { witnesses, instances, acc_witness_w, acc_codewords, acc_instance }) — per-call inputs - Returns PesatTcReduced { pesat, tc, pesat_witness, tc_witness } carrying both phases' reductions for downstream use Internal data flow mirrors the orchestrator exactly: pesat_red.mus / .taus -> TwinConstraintStatement.l1_mus / .l1_taus pesat_witness.codewords -> TwinConstraintProverInputs.fresh_codewords (by reference, no clone) tests/composition_demo.rs: pipeline_runs_pesat_then_twin_constraint_twice exercises the multi-call pattern, asserts both calls succeed, and checks that the reductions differ (confirming no accidental state caching across calls). Generic Lift / Then / WithFlatProof from earlier sketch deliberately left as stubs — generic combinators wait on the verifier-side oracle-handle refactor (open question #3 in module docs) so the abstraction lands once against the final IOR shape. --- src/protocol/composition.rs | 188 ++++++++++++++++++++++++++++++++---- tests/composition_demo.rs | 157 ++++++++++++++++++++++++++++++ 2 files changed, 325 insertions(+), 20 deletions(-) create mode 100644 tests/composition_demo.rs diff --git a/src/protocol/composition.rs b/src/protocol/composition.rs index e47b34c..6bf94ab 100644 --- a/src/protocol/composition.rs +++ b/src/protocol/composition.rs @@ -216,25 +216,173 @@ pub struct WithFlatProof { pub _phantom: std::marker::PhantomData, } -// Trait bounds and impls deliberately omitted from this sketch. +// Generic `Lift` / `Then` / `WithFlatProof` impls deliberately omitted +// for now. The concrete composition below — `PesatTwinConstraint` — +// validates that the GAT-decoupled `IOR` trait actually composes; the +// generic combinators wait on the verifier-side oracle-handle refactor +// (open question #3 above) so the abstraction lands once, against the +// final `IOR` shape. + +// ─── Concrete demo: Pesat → TwinConstraint ──────────────────────────────── // -// Writing `impl<...> Pipeline for Then<...>` requires resolving the -// `Context` + lifetime questions in the module docs first; doing it -// prematurely would bake those decisions into the public surface and -// force a churn-y revision later. Treat this file as a design landing -// pad: extend the types and add impls once a concrete migration target -// is picked (probably starting with the Pesat → TwinConstraint pair, -// the smallest WARP-internal composition that exercises Context-passing -// and `make_next_statement` together). - -// Suppress warnings about the unused IOR / spongefish imports — they -// document the design dependencies even though no code uses them yet. -#[allow(dead_code)] -fn _design_deps_marker( - _: &mut ProverState, - _: &mut VerifierState<'_>, - _: ProverError, - _: VerifierError, - _: &I, -) { +// First real composition built on the GAT'd `IOR`. Holds both phases by +// value so the same pipeline can serve many `prove` calls with +// different witnesses — the lifetime decoupling that motivated the GAT +// migration. Used by `tests/composition_demo.rs`. + +use ark_codes::traits::LinearCode; +use ark_ff::{Field, PrimeField}; +use ark_mt::MerkleHasher; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize}; +use std::marker::PhantomData; + +use crate::protocol::phases::pesat::{ + Pesat, PesatReducedStatement, PesatReducedWitness, PesatStatement, PesatWitness, +}; +use crate::protocol::phases::twin_constraint::{ + TwinConstraint, TwinConstraintProverInputs, TwinConstraintReducedStatement, + TwinConstraintReducedWitness, TwinConstraintStatement, TwinConstraintWitness, +}; +use crate::relations::r1cs::R1CSConstraints; +use crate::types::AccumulatorInstance; + +/// Pipeline value chaining Pesat into TwinConstraint. +/// +/// Holds both phases as data. Constructed once, calls `prove` many +/// times — each call passes new short-lived witnesses without forcing +/// the phase structs (or their `R1CSConstraints` / code / hasher +/// borrows) to be reconstructed. +pub struct PesatTwinConstraint<'phase, F, C, H> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + C: LinearCode, + H: MerkleHasher>, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, +{ + pub pesat: Pesat<'phase, F, C, H>, + pub twin_constraint: TwinConstraint<'phase, F, H>, +} + +impl<'phase, F, C, H> PesatTwinConstraint<'phase, F, C, H> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + C: LinearCode, + H: MerkleHasher>, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, +{ + /// Build the pipeline. Borrows `code`, `hasher`, and `r1cs` for + /// `'phase` — the lifetime of the WARP params, typically. + pub fn new(code: &'phase C, hasher: &'phase H, r1cs: &'phase R1CSConstraints) -> Self { + Self { + pesat: Pesat { + code, + hasher, + _phantom: PhantomData, + }, + twin_constraint: TwinConstraint { + r1cs, + _phantom: PhantomData, + }, + } + } +} + +/// Static config (dimensions) consumed by both phases. Cheap to clone; +/// pass by reference. +pub struct PesatTcConfig { + pub l1: usize, + pub log_l: usize, + pub log_m: usize, + pub log_n: usize, +} + +/// Per-call inputs. Lifetime `'a` is independent of the pipeline's +/// `'phase` — exactly what the GAT migration unlocked. +pub struct PesatTcInputs<'a, F, H> +where + F: Field, + H: MerkleHasher>, +{ + pub witnesses: &'a [Vec], + pub instances: &'a [Vec], + pub acc_witness_w: &'a [Vec], + pub acc_codewords: &'a [Vec], + /// Moves into `TwinConstraintStatement`. Cloned across calls if a + /// single accumulator is reused. + pub acc_instance: AccumulatorInstance, +} + +/// Reduced output of the Pesat → TwinConstraint chain. Holds both +/// phases' reduced statements + reduced witnesses; downstream phases +/// (Ood / Batching / Proximity) read from these. +pub struct PesatTcReduced +where + F: Field, + H: MerkleHasher>, +{ + pub pesat: PesatReducedStatement, + pub tc: TwinConstraintReducedStatement, + pub pesat_witness: PesatReducedWitness, + pub tc_witness: TwinConstraintReducedWitness, +} + +impl<'phase, F, C, H> PesatTwinConstraint<'phase, F, C, H> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + C: LinearCode, + H: MerkleHasher>, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, +{ + /// Run Pesat then TwinConstraint, threading the upstream reduction + /// into the downstream statement and the upstream reduced witness's + /// codewords into the downstream prover inputs. + pub fn prove<'a>( + &self, + prover_state: &mut ProverState, + config: &PesatTcConfig, + inputs: PesatTcInputs<'a, F, H>, + ) -> Result, ProverError> { + let (pesat_red, _, pesat_witness) = self.pesat.prove( + prover_state, + &PesatStatement { + l1: config.l1, + log_m: config.log_m, + }, + &PesatWitness { + witnesses: inputs.witnesses, + }, + &(), + )?; + + // l1_taus is consumed below; clone it for the statement so the + // returned `pesat_red` keeps its own copy via `pesat.taus` (the + // values *are* the same — `reduce_statement` produces them). + let (tc_red, _, tc_witness) = self.twin_constraint.prove( + prover_state, + &TwinConstraintStatement { + acc_instance: inputs.acc_instance, + l1_mus: pesat_red.mus.clone(), + l1_taus: pesat_red.taus.clone(), + log_l: config.log_l, + log_m: config.log_m, + log_n: config.log_n, + }, + &TwinConstraintWitness { + acc_witness_w: inputs.acc_witness_w, + instances: inputs.instances, + witnesses: inputs.witnesses, + }, + &TwinConstraintProverInputs { + fresh_codewords: &pesat_witness.codewords, + acc_codewords: inputs.acc_codewords, + }, + )?; + + Ok(PesatTcReduced { + pesat: pesat_red, + tc: tc_red, + pesat_witness, + tc_witness, + }) + } } diff --git a/tests/composition_demo.rs b/tests/composition_demo.rs new file mode 100644 index 0000000..8b73a60 --- /dev/null +++ b/tests/composition_demo.rs @@ -0,0 +1,157 @@ +//! Demo test for `PesatTwinConstraint` — the first concrete composition +//! built on the GAT-decoupled `IOR` trait. +//! +//! What this validates: +//! +//! 1. The pipeline value holds both phases (`Pesat` + `TwinConstraint`) +//! by data, with their `'phase` lifetime tied to long-lived params. +//! 2. `pipeline.prove(...)` is callable many times in the same scope +//! with **different short-lived witnesses each time**. Pre-GAT +//! migration this would not compile — the witness's lifetime was +//! forced to match the phase struct's lifetime, so the first call's +//! borrows would have been pinned to the pipeline's lifetime, and +//! a second call with different inputs wouldn't have type-checked. +//! 3. The Pesat → TwinConstraint data flow is wired correctly: +//! `pesat_red.mus` / `pesat_red.taus` feed into TC's statement, and +//! `pesat_red_wit.codewords` feeds into TC's prover inputs by +//! reference (no clone). + +use ark_bls12_381::Fr as BLS12_381; +use ark_codes::{ + reed_solomon::{config::ReedSolomonConfig, ReedSolomon}, + traits::LinearCode, +}; +use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; +use ark_ff::UniformRand; +use ark_mt::blake3::Blake3FieldHasher; +use ark_std::rand::thread_rng; + +use warp::protocol::composition::{PesatTcConfig, PesatTcInputs, PesatTwinConstraint}; +use warp::relations::{ + r1cs::hashchain::{compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness}, + BundledPESAT, Relation, ToPolySystem, +}; +use warp::types::AccumulatorInstance; +use warp::utils::poseidon; + +type F = BLS12_381; +type H = Blake3FieldHasher; + +/// Build one `(instance, witness)` batch of size `l1`. +fn make_batch( + l1: usize, + poseidon_config: &ark_crypto_primitives::sponge::poseidon::PoseidonConfig, + hash_chain_size: usize, + rng: &mut impl ark_std::rand::Rng, +) -> (Vec>, Vec>) { + (0..l1) + .map(|_| { + let preimage = vec![F::rand(rng)]; + let instance = HashChainInstance { + digest: compute_hash_chain::>(poseidon_config, &preimage, hash_chain_size), + }; + let witness = HashChainWitness::>::new(preimage); + let relation = HashChainRelation::, CRHGadget<_>>::new( + instance, + witness, + (poseidon_config.clone(), hash_chain_size), + ); + (relation.x, relation.w) + }) + .unzip() +} + +#[test] +fn pipeline_runs_pesat_then_twin_constraint_twice() { + // ── setup: long-lived ───────────────────────────────────────────── + let l1 = 4; + let log_l = (l1 as f64).log2() as usize; + let hash_chain_size = 4; + let mut rng = thread_rng(); + let poseidon_config = poseidon::initialize_poseidon_config::(); + let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + poseidon_config.clone(), + hash_chain_size, + )) + .unwrap(); + let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code = ReedSolomon::new(code_config); + let hasher = Blake3FieldHasher::::new(); + let r1cs_constraints = r1cs.constraints().clone(); + + let n = code.code_len(); + let log_n = (n as f64).log2() as usize; + + let config = PesatTcConfig { + l1, + log_l, + log_m: r1cs.log_m, + log_n, + }; + + // Build the pipeline ONCE — borrows code / hasher / r1cs for 'phase. + let pipeline = PesatTwinConstraint::::new(&code, &hasher, &r1cs_constraints); + + // ── two independent batches with independent lifetimes ──────────── + let (instances_a, witnesses_a) = make_batch(l1, &poseidon_config, hash_chain_size, &mut rng); + let (instances_b, witnesses_b) = make_batch(l1, &poseidon_config, hash_chain_size, &mut rng); + + let acc_w_empty: Vec> = vec![]; + let acc_codewords_empty: Vec> = vec![]; + + // Call A: borrows witnesses_a / instances_a. + let mut prover_state_a = spongefish::domain_separator!("test::composition_demo") + .without_session() + .instance(&0u32) + .std_prover(); + let red_a = pipeline + .prove( + &mut prover_state_a, + &config, + PesatTcInputs { + witnesses: &witnesses_a, + instances: &instances_a, + acc_witness_w: &acc_w_empty, + acc_codewords: &acc_codewords_empty, + acc_instance: AccumulatorInstance::::empty(), + }, + ) + .expect("first prove call should succeed"); + + // Call B: borrows witnesses_b / instances_b. The pipeline value is + // still alive — this is what would not compile without GATs. + let mut prover_state_b = spongefish::domain_separator!("test::composition_demo") + .without_session() + .instance(&0u32) + .std_prover(); + let red_b = pipeline + .prove( + &mut prover_state_b, + &config, + PesatTcInputs { + witnesses: &witnesses_b, + instances: &instances_b, + acc_witness_w: &acc_w_empty, + acc_codewords: &acc_codewords_empty, + acc_instance: AccumulatorInstance::::empty(), + }, + ) + .expect("second prove call should succeed"); + + // Shape sanity — every Pesat run returns l1 mus/taus, every + // TwinConstraint run returns a log_l-vec gamma plus the new + // (zeta_0, beta_tau) pair. + assert_eq!(red_a.pesat.mus.len(), l1); + assert_eq!(red_a.pesat.taus.len(), l1); + assert_eq!(red_a.tc.gamma.len(), log_l); + assert_eq!(red_a.tc.zeta_0.len(), log_n); + + assert_eq!(red_b.pesat.mus.len(), l1); + assert_eq!(red_b.pesat.taus.len(), l1); + assert_eq!(red_b.tc.gamma.len(), log_l); + + // The two calls used independent randomness (different preimages), + // so their reductions should produce different mu vectors — + // confirms the pipeline isn't accidentally caching state across calls. + assert_ne!(red_a.pesat.mus, red_b.pesat.mus); +} From c77b2888b6f767de11c45b405baf912d48696bd5 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 22:46:25 +0200 Subject: [PATCH 43/68] api: drop BoolResult trait, use stdlib then_some(()).ok_or(...) The custom trait wrapped a 6-line function on bool that's already expressible as a 2-call stdlib chain. Each call site goes from cond.ok_or_err(MyError::X)?; to cond.then_some(()).ok_or(MyError::X)?; A few extra characters per site, but no custom abstraction in the public surface, no `use crate::BoolResult` import noise, and one fewer thing for a reader to learn. 13 call sites updated across lib.rs (9), batching.rs (1), proximity.rs (2), and twin_constraint.rs (1). All tests still pass. --- src/lib.rs | 33 +++++++------------------- src/protocol/phases/batching.rs | 3 +-- src/protocol/phases/proximity.rs | 7 +++--- src/protocol/phases/twin_constraint.rs | 3 +-- 4 files changed, 14 insertions(+), 32 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 036266d..3e0c922 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,21 +60,6 @@ use protocol::phases::{ IOR, }; -pub trait BoolResult { - fn ok_or_err(self, err: E) -> Result<(), E>; -} - -impl BoolResult for bool { - #[inline] - fn ok_or_err(self, err: E) -> Result<(), E> { - if self { - Ok(()) - } else { - Err(err) - } - } -} - pub struct WARP, C: LinearCode + Clone, H: MerkleHasher> { pub params: WARPParams, } @@ -432,8 +417,8 @@ where // since they're whole-proof shape checks, not per-oracle opening // validity (which lives behind the IndexedOracle handles). (proof.shift_query_answers.len() == self.params.config.t) - .ok_or_err(VerifierError::NumShiftQueries)?; - (proof.auth_j.len() == l2).ok_or_err(VerifierError::NumL2Instances)?; + .then_some(()).ok_or(VerifierError::NumShiftQueries)?; + (proof.auth_j.len() == l2).then_some(()).ok_or(VerifierError::NumL2Instances)?; // Build (sorted, unique) leaf positions and a map from each unique // position to a row in shift_query_answers (duplicates land on the @@ -531,7 +516,7 @@ where )?; // 10. Accumulator consistency. - (acc_alpha_first == batching_red.alpha).ok_or_err(VerifierError::CodeEvaluationPoint)?; + (acc_alpha_first == batching_red.alpha).then_some(()).ok_or(VerifierError::CodeEvaluationPoint)?; let betas = l2_taus .into_iter() @@ -541,7 +526,7 @@ where .collect::>>(); let beta = scale_and_sum(&betas, &gamma_eq_evals); let expected_beta = concat_slices(&acc_beta_0_first, &acc_beta_1_first); - (expected_beta == beta).ok_or_err(VerifierError::CircuitEvaluationPoint)?; + (expected_beta == beta).then_some(()).ok_or(VerifierError::CircuitEvaluationPoint)?; Ok(()) } @@ -556,13 +541,13 @@ where // Re-encode the witness; check codeword match. let computed_f = self.params.code.encode(&acc_witness.w[0]); - (acc_codeword == &computed_f).ok_or_err(DeciderError::EncodedWitness)?; + (acc_codeword == &computed_f).then_some(()).ok_or(DeciderError::EncodedWitness)?; // Re-commit and check root. let scheme = warp_scheme::(self.params.hasher.clone(), self.params.code.code_len()); let recomputed = scheme.commit(std::slice::from_ref(&computed_f)); - (acc_instance.rt[0] == *recomputed.root()).ok_or_err(DeciderError::MerkleRoot)?; - (acc_witness.td[0].root() == recomputed.root()).ok_or_err(DeciderError::MerkleTrapDoor)?; + (acc_instance.rt[0] == *recomputed.root()).then_some(()).ok_or(DeciderError::MerkleRoot)?; + (acc_witness.td[0].root() == recomputed.root()).then_some(()).ok_or(DeciderError::MerkleTrapDoor)?; // MLE evaluation check. let f_hat = DenseMultilinearExtension::from_evaluations_slice( @@ -570,7 +555,7 @@ where acc_codeword, ); (f_hat.evaluate(&acc_instance.alpha[0]) == acc_instance.mu[0]) - .ok_or_err(DeciderError::MLExtensionEvaluation)?; + .then_some(()).ok_or(DeciderError::MLExtensionEvaluation)?; // Bundled-evaluation check. let tau = &acc_instance.beta.0[0]; @@ -582,7 +567,7 @@ where .p .evaluate_bundled(&tau_zero_evader, &z) .unwrap(); - (computed_eta == acc_instance.eta[0]).ok_or_err(DeciderError::BundledEvaluation)?; + (computed_eta == acc_instance.eta[0]).then_some(()).ok_or(DeciderError::BundledEvaluation)?; Ok(()) } diff --git a/src/protocol/phases/batching.rs b/src/protocol/phases/batching.rs index ac44815..d0a0a71 100644 --- a/src/protocol/phases/batching.rs +++ b/src/protocol/phases/batching.rs @@ -41,7 +41,6 @@ use crate::protocol::oracle::Oracle; use crate::protocol::phases::IOR; use crate::protocol::transcript::EffscVerifierTranscript; use crate::utils::poly::{eq_poly, eq_poly_non_binary}; -use crate::BoolResult; /// [CBBZ23] / HyperPlonk sparse-evaluation optimization: for shift-query /// zetas (indices `1+s..r`), each ζ is a 0/1 vector representing a single @@ -326,7 +325,7 @@ where .into_iter() .zip(&xi_eq_evals) .fold(F::zero(), |acc, (a, b)| acc + a * *b); - (expected == res.final_claim).ok_or_err(VerifierError::Target)?; + (expected == res.final_claim).then_some(()).ok_or(VerifierError::Target)?; Ok((BatchingReductionInputs { alpha: alpha_lsb }, ())) } diff --git a/src/protocol/phases/proximity.rs b/src/protocol/phases/proximity.rs index 520d99a..5ce6eaf 100644 --- a/src/protocol/phases/proximity.rs +++ b/src/protocol/phases/proximity.rs @@ -29,7 +29,6 @@ use crate::error::{ProverError, VerifierError}; use crate::protocol::phases::oracle_handle::IndexedOracle; use crate::protocol::phases::IOR; use crate::protocol::query::QueryIndices; -use crate::BoolResult; pub struct ProximityStatement { pub queries: QueryIndices, @@ -244,7 +243,7 @@ where // Arity / shape checks (orchestrator-side concerns the phase still // owns: number of accumulator openings must match l2; number of // shift queries is implicitly checked by the handle constructors). - (inputs.acc.len() == statement.l2).ok_or_err(VerifierError::NumL2Instances)?; + (inputs.acc.len() == statement.l2).then_some(()).ok_or(VerifierError::NumL2Instances)?; // Validate each oracle handle. The handle is a trait-object // partial function: validate() runs the (lazy, memoized) BCS @@ -252,13 +251,13 @@ where inputs .fresh .validate() - .ok_or_err(VerifierError::ShiftQuery)?; + .then_some(()).ok_or(VerifierError::ShiftQuery)?; count_ops!( MerklePathsVerified, statement.queries.leaf_positions.len() as u64 ); for handle in inputs.acc.iter() { - handle.validate().ok_or_err(VerifierError::ShiftQuery)?; + handle.validate().then_some(()).ok_or(VerifierError::ShiftQuery)?; count_ops!( MerklePathsVerified, statement.queries.leaf_positions.len() as u64 diff --git a/src/protocol/phases/twin_constraint.rs b/src/protocol/phases/twin_constraint.rs index a0f8034..2bad4a2 100644 --- a/src/protocol/phases/twin_constraint.rs +++ b/src/protocol/phases/twin_constraint.rs @@ -58,7 +58,6 @@ use crate::utils::{ poly::{eq_poly, eq_poly_non_binary}, scale_and_sum, }; -use crate::BoolResult; /// Degree-1 polynomial interpolating two field elements: `lo + (hi - lo)·X`. fn linear_poly(lo: F, hi: F) -> DensePolynomial { @@ -236,7 +235,7 @@ impl DeferredOracleCheck { /// transcript segment immediately following the TwinConstraint sumcheck. pub fn discharge(&self, gamma: &[F], nu_0: F, eta: F) -> Result<(), VerifierError> { let expected = eq_poly_non_binary(&self.tau, gamma) * (nu_0 + self.omega * eta); - (expected == self.claim).ok_or_err(VerifierError::Target) + (expected == self.claim).then_some(()).ok_or(VerifierError::Target) } } From 7c1536cdc26d05688da840d33cdec46388922e2f Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 22:53:14 +0200 Subject: [PATCH 44/68] composition: extend pipeline to 3 phases (add Ood + TC->Ood glue) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renames PesatTwinConstraint -> WarpPipeline (it's growing — Batching and Proximity next) and adds the inter-phase work between TwinConstraint and Ood as part of the pipeline's prove(): - compute eta = bundled_pesat.evaluate_bundled(beta_eq_evals, z) - compute nu_0 = tc_witness.f.query_at_point(tc_red.zeta_0) - split tc_witness.z into (new_x, new_w) at the n_minus_k boundary - commit to the reduced codeword as td_new (Merkle root) - absorb (td_new.root, eta, nu_0) into the transcript - run Ood with tc_witness.f as the oracle The pipeline now holds: - pesat, twin_constraint, ood phases - bundled_pesat: &P (for the eta evaluation — generic over BundledPESAT) - code_len (for warp_scheme construction) Output struct WarpPipelineReduced carries all three phases' reductions plus the glue-produced eta, nu_0, td_new, new_x, new_w that the next accumulator state needs. Validates the design point from composition.rs's open questions: inter-phase orchestrator work has a clean home — between phase calls, inside the pipeline's prove() body. Once the generic Then / Lift combinators land, this same logic moves into a dedicated context-step node. For now, hand-written keeps it concrete and readable. tests/composition_demo.rs updated: pipeline_runs_pesat_tc_ood_twice exercises the 3-phase chain with two independent witness batches, asserts shape across all phases (mus, taus, gamma, zeta_0, ood answers/samples) plus the inter-phase glue (new_x.len, new_w.len) and that eta differs between calls (independent randomness flowing all the way through the pipeline). --- src/protocol/composition.rs | 159 +++++++++++++++++++++++++++++------- tests/composition_demo.rs | 38 +++++---- 2 files changed, 151 insertions(+), 46 deletions(-) diff --git a/src/protocol/composition.rs b/src/protocol/composition.rs index 6bf94ab..33ec13f 100644 --- a/src/protocol/composition.rs +++ b/src/protocol/composition.rs @@ -236,6 +236,11 @@ use ark_mt::MerkleHasher; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize}; use std::marker::PhantomData; +use effsc::hypercube::compute_hypercube_eq_evals; + +use crate::count_ops; +use crate::crypto::merkle::{warp_scheme, WarpCommitted}; +use crate::protocol::phases::ood::{Ood, OodProverInputs, OodReducedStatement, OodStatement}; use crate::protocol::phases::pesat::{ Pesat, PesatReducedStatement, PesatReducedWitness, PesatStatement, PesatWitness, }; @@ -244,35 +249,55 @@ use crate::protocol::phases::twin_constraint::{ TwinConstraintReducedWitness, TwinConstraintStatement, TwinConstraintWitness, }; use crate::relations::r1cs::R1CSConstraints; +use crate::relations::BundledPESAT; use crate::types::AccumulatorInstance; -/// Pipeline value chaining Pesat into TwinConstraint. +/// Growing pipeline value: Pesat → TwinConstraint → Ood. +/// +/// Holds three phases as data plus the few `WARPParams` references +/// the inter-phase glue needs (`bundled_pesat`, `hasher`, `code_len`). +/// Constructed once, `prove` is callable many times with short-lived +/// per-batch inputs — the GAT decoupling on [`crate::protocol::phases::IOR`] +/// keeps that ergonomic. /// -/// Holds both phases as data. Constructed once, calls `prove` many -/// times — each call passes new short-lived witnesses without forcing -/// the phase structs (or their `R1CSConstraints` / code / hasher -/// borrows) to be reconstructed. -pub struct PesatTwinConstraint<'phase, F, C, H> +/// Will grow: Batching next (with the DAG fan-in from `zeta_0` + +/// `samples_flat` + sampled queries), then Proximity, at which point +/// this struct becomes the full replacement for `WARP::prove`. +pub struct WarpPipeline<'phase, F, P, C, H> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + P: BundledPESAT, C: LinearCode, H: MerkleHasher>, H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { pub pesat: Pesat<'phase, F, C, H>, pub twin_constraint: TwinConstraint<'phase, F, H>, + pub ood: Ood<'phase, F>, + /// `BundledPESAT` for the inter-phase `eta` evaluation between TC + /// and Ood. Kept by reference so the pipeline value doesn't own + /// the relation. + pub bundled_pesat: &'phase P, + /// Cached for building the new-oracle Merkle commit between TC + /// and Ood (`warp_scheme(hasher.clone(), code_len)`). + pub code_len: usize, } -impl<'phase, F, C, H> PesatTwinConstraint<'phase, F, C, H> +impl<'phase, F, P, C, H> WarpPipeline<'phase, F, P, C, H> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + P: BundledPESAT, C: LinearCode, H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize + Clone + Eq, { - /// Build the pipeline. Borrows `code`, `hasher`, and `r1cs` for - /// `'phase` — the lifetime of the WARP params, typically. - pub fn new(code: &'phase C, hasher: &'phase H, r1cs: &'phase R1CSConstraints) -> Self { + pub fn new( + code: &'phase C, + hasher: &'phase H, + r1cs: &'phase R1CSConstraints, + bundled_pesat: &'phase P, + code_len: usize, + ) -> Self { Self { pesat: Pesat { code, @@ -283,22 +308,30 @@ where r1cs, _phantom: PhantomData, }, + ood: Ood::new(), + bundled_pesat, + code_len, } } } -/// Static config (dimensions) consumed by both phases. Cheap to clone; -/// pass by reference. -pub struct PesatTcConfig { +/// Static config (dimensions + per-phase tuning). Cheap to pass by +/// reference; covers all phases through Ood. +pub struct WarpPipelineConfig { pub l1: usize, pub log_l: usize, pub log_m: usize, pub log_n: usize, + /// Witness arity: `n - k` instance variables. Used to split the + /// reduced-z vector at the TC→Ood boundary. + pub n_minus_k: usize, + /// Number of OOD samples (Ood phase). + pub s: usize, } /// Per-call inputs. Lifetime `'a` is independent of the pipeline's /// `'phase` — exactly what the GAT migration unlocked. -pub struct PesatTcInputs<'a, F, H> +pub struct WarpPipelineInputs<'a, F, H> where F: Field, H: MerkleHasher>, @@ -312,36 +345,61 @@ where pub acc_instance: AccumulatorInstance, } -/// Reduced output of the Pesat → TwinConstraint chain. Holds both -/// phases' reduced statements + reduced witnesses; downstream phases -/// (Ood / Batching / Proximity) read from these. -pub struct PesatTcReduced +/// Reduced output of the prefix pipeline. Holds the per-phase +/// reductions plus the TC→Ood inter-phase published values +/// (`eta`, `nu_0`, `td_new`) that downstream Batching/Proximity will +/// consume — and that the new accumulator instance carries. +pub struct WarpPipelineReduced where F: Field, H: MerkleHasher>, { pub pesat: PesatReducedStatement, pub tc: TwinConstraintReducedStatement, + pub ood: OodReducedStatement, pub pesat_witness: PesatReducedWitness, pub tc_witness: TwinConstraintReducedWitness, + /// Bundled-PESAT evaluation of the reduced witness — the `η` value + /// absorbed between TC and Ood. + pub eta: F, + /// Reduced-oracle code-eval at `tc.zeta_0` — the `ν₀` absorbed + /// between TC and Ood. + pub nu_0: F, + /// New (single-codeword) Merkle commitment to the reduced + /// codeword. Becomes the next accumulator's root. + pub td_new: WarpCommitted, + /// Split halves of the reduced witness vector `z`: `(new_x, new_w)`. + /// Become the instance/witness components of the next accumulator. + pub new_x: Vec, + pub new_w: Vec, } -impl<'phase, F, C, H> PesatTwinConstraint<'phase, F, C, H> +impl<'phase, F, P, C, H> WarpPipeline<'phase, F, P, C, H> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + P: BundledPESAT, C: LinearCode, H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize + Clone + Eq, { - /// Run Pesat then TwinConstraint, threading the upstream reduction - /// into the downstream statement and the upstream reduced witness's - /// codewords into the downstream prover inputs. + /// Run Pesat → TwinConstraint → (inter-phase glue) → Ood. + /// + /// Mirrors the orchestrator's data flow exactly: + /// - `pesat_red.mus / .taus` feed TC's `Statement` + /// - `pesat_witness.codewords` feed TC's `ProverInputs` by reference + /// - between TC and Ood: compute `eta` (bundled-PESAT eval), + /// `nu_0` (oracle eval at `tc.zeta_0`), commit to the reduced + /// codeword as `td_new`, absorb `(td_new.root, eta, nu_0)` into + /// the transcript + /// - Ood reads its samples / answers from the transcript using + /// `tc_witness.f` as the oracle pub fn prove<'a>( &self, prover_state: &mut ProverState, - config: &PesatTcConfig, - inputs: PesatTcInputs<'a, F, H>, - ) -> Result, ProverError> { + config: &WarpPipelineConfig, + inputs: WarpPipelineInputs<'a, F, H>, + ) -> Result, ProverError> { + // ── Phase 2: PESAT ───────────────────────────────────────────── let (pesat_red, _, pesat_witness) = self.pesat.prove( prover_state, &PesatStatement { @@ -354,9 +412,7 @@ where &(), )?; - // l1_taus is consumed below; clone it for the statement so the - // returned `pesat_red` keeps its own copy via `pesat.taus` (the - // values *are* the same — `reduce_statement` produces them). + // ── Phase 3a: twin-constraint sumcheck ───────────────────────── let (tc_red, _, tc_witness) = self.twin_constraint.prove( prover_state, &TwinConstraintStatement { @@ -378,11 +434,52 @@ where }, )?; - Ok(PesatTcReduced { + // ── Phase 3b: TC→Ood glue (eta, nu_0, td_new commit, absorb) ── + let beta_eq_evals = compute_hypercube_eq_evals(config.log_m, &tc_red.beta_tau); + let eta = self + .bundled_pesat + .evaluate_bundled(&beta_eq_evals, &tc_witness.z) + .map_err(|_| ProverError::SpongeFish)?; + let nu_0 = tc_witness.f.query_at_point(&tc_red.zeta_0); + + let (new_x_slice, new_w_slice) = tc_witness.z.split_at(config.n_minus_k); + let new_x = new_x_slice.to_vec(); + let new_w = new_w_slice.to_vec(); + + let td_new = { + let _s = tracing::info_span!("warp_pipeline.commit_new_oracle").entered(); + count_ops!(MerkleTreeBuilds); + let scheme = warp_scheme::(self.pesat.hasher.clone(), self.code_len); + scheme.commit(&[tc_witness.f.evals().to_vec()]) + }; + prover_state.prover_message(td_new.root()); + prover_state.prover_message(&eta); + prover_state.prover_message(&nu_0); + + // ── Phase 3c: OOD ───────────────────────────────────────────── + let (ood_red, _, _) = self.ood.prove( + prover_state, + &OodStatement { + s: config.s, + log_n: config.log_n, + }, + &(), + &OodProverInputs { + oracle: &tc_witness.f, + }, + )?; + + Ok(WarpPipelineReduced { pesat: pesat_red, tc: tc_red, + ood: ood_red, pesat_witness, tc_witness, + eta, + nu_0, + td_new, + new_x, + new_w, }) } } diff --git a/tests/composition_demo.rs b/tests/composition_demo.rs index 8b73a60..d1f03b1 100644 --- a/tests/composition_demo.rs +++ b/tests/composition_demo.rs @@ -26,7 +26,7 @@ use ark_ff::UniformRand; use ark_mt::blake3::Blake3FieldHasher; use ark_std::rand::thread_rng; -use warp::protocol::composition::{PesatTcConfig, PesatTcInputs, PesatTwinConstraint}; +use warp::protocol::composition::{WarpPipeline, WarpPipelineConfig, WarpPipelineInputs}; use warp::relations::{ r1cs::hashchain::{compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness}, BundledPESAT, Relation, ToPolySystem, @@ -62,9 +62,10 @@ fn make_batch( } #[test] -fn pipeline_runs_pesat_then_twin_constraint_twice() { +fn pipeline_runs_pesat_tc_ood_twice() { // ── setup: long-lived ───────────────────────────────────────────── let l1 = 4; + let s = 8; let log_l = (l1 as f64).log2() as usize; let hash_chain_size = 4; let mut rng = thread_rng(); @@ -82,15 +83,19 @@ fn pipeline_runs_pesat_then_twin_constraint_twice() { let n = code.code_len(); let log_n = (n as f64).log2() as usize; - let config = PesatTcConfig { + let config = WarpPipelineConfig { l1, log_l, log_m: r1cs.log_m, log_n, + n_minus_k: r1cs.n - r1cs.k, + s, }; - // Build the pipeline ONCE — borrows code / hasher / r1cs for 'phase. - let pipeline = PesatTwinConstraint::::new(&code, &hasher, &r1cs_constraints); + // Build the pipeline ONCE — borrows code / hasher / r1cs / bundled + // PESAT relation for 'phase. + let pipeline = + WarpPipeline::::new(&code, &hasher, &r1cs_constraints, &r1cs, n); // ── two independent batches with independent lifetimes ──────────── let (instances_a, witnesses_a) = make_batch(l1, &poseidon_config, hash_chain_size, &mut rng); @@ -108,7 +113,7 @@ fn pipeline_runs_pesat_then_twin_constraint_twice() { .prove( &mut prover_state_a, &config, - PesatTcInputs { + WarpPipelineInputs { witnesses: &witnesses_a, instances: &instances_a, acc_witness_w: &acc_w_empty, @@ -128,7 +133,7 @@ fn pipeline_runs_pesat_then_twin_constraint_twice() { .prove( &mut prover_state_b, &config, - PesatTcInputs { + WarpPipelineInputs { witnesses: &witnesses_b, instances: &instances_b, acc_witness_w: &acc_w_empty, @@ -138,20 +143,23 @@ fn pipeline_runs_pesat_then_twin_constraint_twice() { ) .expect("second prove call should succeed"); - // Shape sanity — every Pesat run returns l1 mus/taus, every - // TwinConstraint run returns a log_l-vec gamma plus the new - // (zeta_0, beta_tau) pair. + // Shape sanity across all three phases. assert_eq!(red_a.pesat.mus.len(), l1); assert_eq!(red_a.pesat.taus.len(), l1); assert_eq!(red_a.tc.gamma.len(), log_l); assert_eq!(red_a.tc.zeta_0.len(), log_n); + // Ood: s answers, samples_flat is s * log_n long. + assert_eq!(red_a.ood.answers.len(), s); + assert_eq!(red_a.ood.samples_flat.len(), s * log_n); + + // Inter-phase glue produced sensible split halves. + assert_eq!(red_a.new_x.len(), r1cs.n - r1cs.k); + assert_eq!(red_a.new_w.len(), r1cs.k); assert_eq!(red_b.pesat.mus.len(), l1); - assert_eq!(red_b.pesat.taus.len(), l1); - assert_eq!(red_b.tc.gamma.len(), log_l); + assert_eq!(red_b.ood.answers.len(), s); - // The two calls used independent randomness (different preimages), - // so their reductions should produce different mu vectors — - // confirms the pipeline isn't accidentally caching state across calls. + // Independent randomness across calls => reductions differ. assert_ne!(red_a.pesat.mus, red_b.pesat.mus); + assert_ne!(red_a.eta, red_b.eta); } From 421b48585ec360528a27e72e08668b0ebc72a996 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 22:57:21 +0200 Subject: [PATCH 45/68] composition: extend pipeline to 4 phases (add Batching + Ood->Batching glue) Pipeline now runs Pesat -> TwinConstraint -> (TC->Ood glue) -> Ood -> (Ood->Batching glue) -> Batching. Only Proximity remains. Ood->Batching glue is small: sample t shift query indices via QueryIndices::sample. Then Batching consumes the DAG fan-in (tc.zeta_0 + ood.samples_flat + queries.evaluation_points) via the existing BatchingStatement::from_phase_outputs constructor. WarpPipelineReduced gains: batching reduced statement, sampled queries, batching's mu (becomes the next accumulator's mu). Test now asserts shapes through Batching (queries.len == t, batching.alpha.len == log_n, batching_mu differs across calls). --- src/protocol/composition.rs | 56 +++++++++++++++++++++++++++++++------ tests/composition_demo.rs | 10 +++++++ 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/protocol/composition.rs b/src/protocol/composition.rs index 33ec13f..acf60b7 100644 --- a/src/protocol/composition.rs +++ b/src/protocol/composition.rs @@ -240,6 +240,9 @@ use effsc::hypercube::compute_hypercube_eq_evals; use crate::count_ops; use crate::crypto::merkle::{warp_scheme, WarpCommitted}; +use crate::protocol::phases::batching::{ + Batching, BatchingProverInputs, BatchingReducedStatement, BatchingStatement, +}; use crate::protocol::phases::ood::{Ood, OodProverInputs, OodReducedStatement, OodStatement}; use crate::protocol::phases::pesat::{ Pesat, PesatReducedStatement, PesatReducedWitness, PesatStatement, PesatWitness, @@ -248,21 +251,21 @@ use crate::protocol::phases::twin_constraint::{ TwinConstraint, TwinConstraintProverInputs, TwinConstraintReducedStatement, TwinConstraintReducedWitness, TwinConstraintStatement, TwinConstraintWitness, }; +use crate::protocol::query::QueryIndices; use crate::relations::r1cs::R1CSConstraints; use crate::relations::BundledPESAT; use crate::types::AccumulatorInstance; -/// Growing pipeline value: Pesat → TwinConstraint → Ood. +/// Growing pipeline value: Pesat → TwinConstraint → Ood → Batching. /// -/// Holds three phases as data plus the few `WARPParams` references +/// Holds four phases as data plus the few `WARPParams` references /// the inter-phase glue needs (`bundled_pesat`, `hasher`, `code_len`). /// Constructed once, `prove` is callable many times with short-lived /// per-batch inputs — the GAT decoupling on [`crate::protocol::phases::IOR`] /// keeps that ergonomic. /// -/// Will grow: Batching next (with the DAG fan-in from `zeta_0` + -/// `samples_flat` + sampled queries), then Proximity, at which point -/// this struct becomes the full replacement for `WARP::prove`. +/// Will grow once more: Proximity next, at which point this struct +/// becomes the full replacement for `WARP::prove`. pub struct WarpPipeline<'phase, F, P, C, H> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, @@ -274,6 +277,7 @@ where pub pesat: Pesat<'phase, F, C, H>, pub twin_constraint: TwinConstraint<'phase, F, H>, pub ood: Ood<'phase, F>, + pub batching: Batching<'phase, F>, /// `BundledPESAT` for the inter-phase `eta` evaluation between TC /// and Ood. Kept by reference so the pipeline value doesn't own /// the relation. @@ -309,6 +313,7 @@ where _phantom: PhantomData, }, ood: Ood::new(), + batching: Batching::new(), bundled_pesat, code_len, } @@ -316,7 +321,7 @@ where } /// Static config (dimensions + per-phase tuning). Cheap to pass by -/// reference; covers all phases through Ood. +/// reference; covers all phases through Batching. pub struct WarpPipelineConfig { pub l1: usize, pub log_l: usize, @@ -327,6 +332,8 @@ pub struct WarpPipelineConfig { pub n_minus_k: usize, /// Number of OOD samples (Ood phase). pub s: usize, + /// Number of shift queries (Batching + Proximity phases). + pub t: usize, } /// Per-call inputs. Lifetime `'a` is independent of the pipeline's @@ -346,9 +353,9 @@ where } /// Reduced output of the prefix pipeline. Holds the per-phase -/// reductions plus the TC→Ood inter-phase published values -/// (`eta`, `nu_0`, `td_new`) that downstream Batching/Proximity will -/// consume — and that the new accumulator instance carries. +/// reductions plus the inter-phase published values (`eta`, `nu_0`, +/// `td_new`, `queries`, batching's `mu`) that downstream Proximity and +/// the next accumulator instance will consume. pub struct WarpPipelineReduced where F: Field, @@ -357,6 +364,7 @@ where pub pesat: PesatReducedStatement, pub tc: TwinConstraintReducedStatement, pub ood: OodReducedStatement, + pub batching: BatchingReducedStatement, pub pesat_witness: PesatReducedWitness, pub tc_witness: TwinConstraintReducedWitness, /// Bundled-PESAT evaluation of the reduced witness — the `η` value @@ -372,6 +380,12 @@ where /// Become the instance/witness components of the next accumulator. pub new_x: Vec, pub new_w: Vec, + /// Shift query indices sampled between Ood and Batching. Carried + /// forward for Proximity (the next phase to land in this pipeline). + pub queries: QueryIndices, + /// Batching's reduced witness `μ = f̂(α)`. Becomes the next + /// accumulator's `μ`. + pub batching_mu: F, } impl<'phase, F, P, C, H> WarpPipeline<'phase, F, P, C, H> @@ -469,10 +483,32 @@ where }, )?; + // ── Phase 3c→3d glue: sample shift query indices ────────────── + let queries = QueryIndices::::sample(prover_state, config.log_n, config.t); + + // ── Phase 3d: batching sumcheck (DAG fan-in from tc.zeta_0 + + // ood.samples_flat + queries.evaluation_points) ─────────────── + let (batching_red, _, batching_witness) = self.batching.prove( + prover_state, + &BatchingStatement::from_phase_outputs( + tc_red.zeta_0.clone(), + &ood_red.samples_flat, + &queries.evaluation_points, + config.s, + config.t, + config.log_n, + ), + &(), + &BatchingProverInputs { + oracle: &tc_witness.f, + }, + )?; + Ok(WarpPipelineReduced { pesat: pesat_red, tc: tc_red, ood: ood_red, + batching: batching_red, pesat_witness, tc_witness, eta, @@ -480,6 +516,8 @@ where td_new, new_x, new_w, + queries, + batching_mu: batching_witness.mu, }) } } diff --git a/tests/composition_demo.rs b/tests/composition_demo.rs index d1f03b1..ac289c0 100644 --- a/tests/composition_demo.rs +++ b/tests/composition_demo.rs @@ -66,6 +66,7 @@ fn pipeline_runs_pesat_tc_ood_twice() { // ── setup: long-lived ───────────────────────────────────────────── let l1 = 4; let s = 8; + let t = 7; let log_l = (l1 as f64).log2() as usize; let hash_chain_size = 4; let mut rng = thread_rng(); @@ -90,6 +91,7 @@ fn pipeline_runs_pesat_tc_ood_twice() { log_n, n_minus_k: r1cs.n - r1cs.k, s, + t, }; // Build the pipeline ONCE — borrows code / hasher / r1cs / bundled @@ -156,10 +158,18 @@ fn pipeline_runs_pesat_tc_ood_twice() { assert_eq!(red_a.new_x.len(), r1cs.n - r1cs.k); assert_eq!(red_a.new_w.len(), r1cs.k); + // Batching: t shift queries sampled, alpha is the new code-eval point + // (a log_n-length challenge vector). + assert_eq!(red_a.queries.evaluation_points.len(), t); + assert_eq!(red_a.queries.leaf_positions.len(), t); + assert_eq!(red_a.batching.alpha.len(), log_n); + assert_eq!(red_b.pesat.mus.len(), l1); assert_eq!(red_b.ood.answers.len(), s); + assert_eq!(red_b.queries.evaluation_points.len(), t); // Independent randomness across calls => reductions differ. assert_ne!(red_a.pesat.mus, red_b.pesat.mus); assert_ne!(red_a.eta, red_b.eta); + assert_ne!(red_a.batching_mu, red_b.batching_mu); } From fe3d5fd10bbdfc31fdbad01ce28f0a27cae5aa33 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 23:05:37 +0200 Subject: [PATCH 46/68] composition: extend pipeline to 5 phases (add Proximity) Pipeline now runs the full prove path: Pesat -> TwinConstraint -> (TC->Ood glue) -> Ood -> (Ood->Batching glue) -> Batching -> Proximity. Adds proximity field to WarpPipeline (constructed inline since it just borrows the pipeline's hasher), l2 to WarpPipelineConfig, acc_td slice to WarpPipelineInputs (the per-accumulated-instance Merkle commitments that Proximity opens), and proximity_proof to WarpPipelineReduced (the per-commitment opening proofs + flat shift-query answer table that go straight into WARPProof). Test asserts Proximity output shapes: shift_query_answers is t-by-(l2+l1), auth_j has l2 entries, plus existing assertions for the upstream phases. Step 4 of the migration plan complete; next is step 5: replace WARP::prove's body with a pipeline.prove() call so the duplication disappears. --- src/protocol/composition.rs | 59 ++++++++++++++++++++++++++++++------- tests/composition_demo.rs | 12 ++++++++ 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/src/protocol/composition.rs b/src/protocol/composition.rs index acf60b7..ef12e79 100644 --- a/src/protocol/composition.rs +++ b/src/protocol/composition.rs @@ -247,6 +247,9 @@ use crate::protocol::phases::ood::{Ood, OodProverInputs, OodReducedStatement, Oo use crate::protocol::phases::pesat::{ Pesat, PesatReducedStatement, PesatReducedWitness, PesatStatement, PesatWitness, }; +use crate::protocol::phases::proximity::{ + Proximity, ProximityProofString, ProximityProverInputs, ProximityStatement, +}; use crate::protocol::phases::twin_constraint::{ TwinConstraint, TwinConstraintProverInputs, TwinConstraintReducedStatement, TwinConstraintReducedWitness, TwinConstraintStatement, TwinConstraintWitness, @@ -256,16 +259,18 @@ use crate::relations::r1cs::R1CSConstraints; use crate::relations::BundledPESAT; use crate::types::AccumulatorInstance; -/// Growing pipeline value: Pesat → TwinConstraint → Ood → Batching. +/// Full prove-side pipeline value: Pesat → TwinConstraint → Ood → +/// Batching → Proximity. /// -/// Holds four phases as data plus the few `WARPParams` references +/// Holds all five phases as data plus the few `WARPParams` references /// the inter-phase glue needs (`bundled_pesat`, `hasher`, `code_len`). /// Constructed once, `prove` is callable many times with short-lived /// per-batch inputs — the GAT decoupling on [`crate::protocol::phases::IOR`] /// keeps that ergonomic. /// -/// Will grow once more: Proximity next, at which point this struct -/// becomes the full replacement for `WARP::prove`. +/// At this point the pipeline expresses the full prover; `WARP::prove` +/// becomes a thin wrapper that builds inputs, runs the pipeline, and +/// assembles the global proof object from the pipeline's output. pub struct WarpPipeline<'phase, F, P, C, H> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, @@ -278,6 +283,7 @@ where pub twin_constraint: TwinConstraint<'phase, F, H>, pub ood: Ood<'phase, F>, pub batching: Batching<'phase, F>, + pub proximity: Proximity<'phase, F, H>, /// `BundledPESAT` for the inter-phase `eta` evaluation between TC /// and Ood. Kept by reference so the pipeline value doesn't own /// the relation. @@ -314,6 +320,10 @@ where }, ood: Ood::new(), batching: Batching::new(), + proximity: Proximity { + hasher, + _phantom: PhantomData, + }, bundled_pesat, code_len, } @@ -321,7 +331,7 @@ where } /// Static config (dimensions + per-phase tuning). Cheap to pass by -/// reference; covers all phases through Batching. +/// reference; covers all five phases. pub struct WarpPipelineConfig { pub l1: usize, pub log_l: usize, @@ -334,6 +344,8 @@ pub struct WarpPipelineConfig { pub s: usize, /// Number of shift queries (Batching + Proximity phases). pub t: usize, + /// Number of accumulated instances (`l - l1`). Used by Proximity. + pub l2: usize, } /// Per-call inputs. Lifetime `'a` is independent of the pipeline's @@ -347,15 +359,19 @@ where pub instances: &'a [Vec], pub acc_witness_w: &'a [Vec], pub acc_codewords: &'a [Vec], + /// Accumulated commitments (one per accumulated entry). Read by + /// Proximity for shift-query openings. + pub acc_td: &'a [WarpCommitted], /// Moves into `TwinConstraintStatement`. Cloned across calls if a /// single accumulator is reused. pub acc_instance: AccumulatorInstance, } -/// Reduced output of the prefix pipeline. Holds the per-phase -/// reductions plus the inter-phase published values (`eta`, `nu_0`, -/// `td_new`, `queries`, batching's `mu`) that downstream Proximity and -/// the next accumulator instance will consume. +/// Full output of the prove-side pipeline. Holds per-phase reductions, +/// inter-phase published values (`eta`, `nu_0`, `td_new`, +/// batching's `mu`), and Proximity's proof string. The orchestrator +/// (`WARP::prove`) assembles the new accumulator instance and the +/// global `WARPProof` from these. pub struct WarpPipelineReduced where F: Field, @@ -380,12 +396,16 @@ where /// Become the instance/witness components of the next accumulator. pub new_x: Vec, pub new_w: Vec, - /// Shift query indices sampled between Ood and Batching. Carried - /// forward for Proximity (the next phase to land in this pipeline). + /// Shift query indices sampled between Ood and Batching. Used by + /// the verifier to reconstruct the same query positions and as + /// part of the data flow into Proximity. pub queries: QueryIndices, /// Batching's reduced witness `μ = f̂(α)`. Becomes the next /// accumulator's `μ`. pub batching_mu: F, + /// Proximity proof string: per-commitment opening proofs and the + /// flat shift-query answer table. Goes straight into `WARPProof`. + pub proximity_proof: ProximityProofString, } impl<'phase, F, P, C, H> WarpPipeline<'phase, F, P, C, H> @@ -504,6 +524,22 @@ where }, )?; + // ── Phase 3e: proximity (shift-query openings) ──────────────── + let (_, proximity_proof, _) = self.proximity.prove( + prover_state, + &ProximityStatement { + queries: queries.clone(), + l2: config.l2, + t: config.t, + n: self.code_len, + }, + &(), + &ProximityProverInputs { + td_0: &pesat_witness.td_0, + acc_td: inputs.acc_td, + }, + )?; + Ok(WarpPipelineReduced { pesat: pesat_red, tc: tc_red, @@ -518,6 +554,7 @@ where new_w, queries, batching_mu: batching_witness.mu, + proximity_proof, }) } } diff --git a/tests/composition_demo.rs b/tests/composition_demo.rs index ac289c0..497dddf 100644 --- a/tests/composition_demo.rs +++ b/tests/composition_demo.rs @@ -92,6 +92,7 @@ fn pipeline_runs_pesat_tc_ood_twice() { n_minus_k: r1cs.n - r1cs.k, s, t, + l2: 0, }; // Build the pipeline ONCE — borrows code / hasher / r1cs / bundled @@ -120,6 +121,7 @@ fn pipeline_runs_pesat_tc_ood_twice() { instances: &instances_a, acc_witness_w: &acc_w_empty, acc_codewords: &acc_codewords_empty, + acc_td: &[], acc_instance: AccumulatorInstance::::empty(), }, ) @@ -140,6 +142,7 @@ fn pipeline_runs_pesat_tc_ood_twice() { instances: &instances_b, acc_witness_w: &acc_w_empty, acc_codewords: &acc_codewords_empty, + acc_td: &[], acc_instance: AccumulatorInstance::::empty(), }, ) @@ -168,6 +171,15 @@ fn pipeline_runs_pesat_tc_ood_twice() { assert_eq!(red_b.ood.answers.len(), s); assert_eq!(red_b.queries.evaluation_points.len(), t); + // Proximity proof string: per-query-row size = (l2 + l1) and + // outer length = t. + assert_eq!(red_a.proximity_proof.shift_query_answers.len(), t); + assert_eq!( + red_a.proximity_proof.shift_query_answers[0].len(), + config.l2 + l1 + ); + assert_eq!(red_a.proximity_proof.auth_j.len(), config.l2); + // Independent randomness across calls => reductions differ. assert_ne!(red_a.pesat.mus, red_b.pesat.mus); assert_ne!(red_a.eta, red_b.eta); From 399357bffa2c3378136cd9ddc27ec8f572834abe Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 23:27:39 +0200 Subject: [PATCH 47/68] composition: replace WARP::prove body with WarpPipeline.prove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 5-phase WarpPipeline (Pesat → TwinConstraint → Ood → Batching → Proximity) in src/protocol/composition.rs already encapsulates the entire prove-side data flow including the inter-phase glue (eta, nu_0, new-oracle commit, shift-query sampling). The old WARP::prove body duplicated that logic verbatim. Rewrite WARP::prove to: - Keep the global before-work: input validation, absorb_instances, and acc_instance.absorb_into. - Destructure acc_witness into acc_tds / acc_ws / acc_fs. - Build a WarpPipeline + WarpPipelineConfig and call pipeline.prove(...) with WarpPipelineInputs. - Assemble the new AccumulatorInstance / AccumulatorWitness / WARPProof from the pipeline's WarpPipelineReduced output. Also drop the now-unused phase-internal imports (BatchingProverInputs, OodProverInputs, PesatWitness, ProximityProverInputs, TwinConstraintProverInputs, TwinConstraintWitness) — the verifier still uses the verify-side ones. Net: -88 lines on src/lib.rs. WARP::verify and WARP::decide are untouched. All tests pass (3 lib unit, 2 integration_warp, 9 verifier_negative, 1 composition_demo) and cargo bench --no-run is clean. --- src/lib.rs | 184 ++++++++++++++--------------------------------------- 1 file changed, 48 insertions(+), 136 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3e0c922..38895c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,15 +48,14 @@ pub mod prelude { use crate::crypto::merkle::warp_scheme; use error::{DeciderError, ProverError, VerifierError}; +use protocol::composition::{WarpPipeline, WarpPipelineConfig, WarpPipelineInputs}; use protocol::phases::{ - batching::{Batching, BatchingProverInputs, BatchingStatement, BatchingVerifierInputs}, - ood::{Ood, OodProverInputs, OodStatement}, + batching::{Batching, BatchingStatement, BatchingVerifierInputs}, + ood::{Ood, OodStatement}, oracle_handle::MerkleIndexedOracle, - pesat::{Pesat, PesatStatement, PesatWitness}, - proximity::{Proximity, ProximityProverInputs, ProximityStatement, ProximityVerifierInputs}, - twin_constraint::{ - TwinConstraint, TwinConstraintProverInputs, TwinConstraintStatement, TwinConstraintWitness, - }, + pesat::{Pesat, PesatStatement}, + proximity::{Proximity, ProximityStatement, ProximityVerifierInputs}, + twin_constraint::{TwinConstraint, TwinConstraintStatement}, IOR, }; @@ -178,152 +177,65 @@ where } = acc_witness; let acc_fs: Vec> = acc_tds.iter().map(|td| td.codewords()[0].clone()).collect(); - // Phase 2: PESAT - let pesat_phase = Pesat:: { - code: &self.params.code, - hasher: &self.params.hasher, - _phantom: PhantomData, + // Build and run the full prove-side pipeline (Pesat → TwinConstraint + // → Ood → Batching → Proximity). The orchestrator's only remaining + // duty is the global before/after work: input validation, instance + // absorption, and assembling the new accumulator state plus the + // global `WARPProof` from the pipeline's reduced output. + let pipeline = WarpPipeline::new( + &self.params.code, + &self.params.hasher, + self.params.p.constraints(), + &self.params.p, + n, + ); + let config = WarpPipelineConfig { + l1, + log_l, + log_m, + log_n, + n_minus_k: N - k, + s: self.params.config.s, + t: self.params.config.t, + l2, }; - let (pesat_red, _pesat_proof, pesat_red_wit) = pesat_phase.prove( + let reduced = pipeline.prove( prover_state, - &PesatStatement { l1, log_m }, - &PesatWitness { + &config, + WarpPipelineInputs { witnesses: &witnesses, - }, - &(), - )?; - - // Phase 3a: twin-constraint sumcheck - let tc_phase = TwinConstraint:: { - r1cs: self.params.p.constraints(), - _phantom: PhantomData, - }; - let (tc_red, _tc_proof, tc_red_wit) = tc_phase.prove( - prover_state, - &TwinConstraintStatement { - acc_instance, - l1_mus: pesat_red.mus.clone(), - l1_taus: pesat_red.taus, - log_l, - log_m, - log_n, - }, - &TwinConstraintWitness { - acc_witness_w: &acc_ws, instances: &instances, - witnesses: &witnesses, - }, - &TwinConstraintProverInputs { - fresh_codewords: &pesat_red_wit.codewords, + acc_witness_w: &acc_ws, acc_codewords: &acc_fs, - }, - )?; - - // Phase 3b: bundled η, ν₀, new commitment, absorb. - let beta_eq_evals = compute_hypercube_eq_evals(log_m, &tc_red.beta_tau); - let eta = self - .params - .p - .evaluate_bundled(&beta_eq_evals, &tc_red_wit.z) - .map_err(|_| ProverError::SpongeFish)?; - let nu_0 = tc_red_wit.f.query_at_point(&tc_red.zeta_0); - - let (new_x, new_w) = tc_red_wit.z.split_at(N - k); - let new_x = new_x.to_vec(); - let new_w = new_w.to_vec(); - let new_beta = (vec![tc_red.beta_tau.clone()], vec![new_x]); - - // Commit to the new (single-codeword) reduced oracle. - let td_new = { - let _s = tracing::info_span!("warp.commit_new_oracle").entered(); - count_ops!(MerkleTreeBuilds); - let scheme = warp_scheme::(self.params.hasher.clone(), n); - let new_codeword = tc_red_wit.f.evals().to_vec(); - scheme.commit(&[new_codeword]) - }; - prover_state.prover_message(td_new.root()); - prover_state.prover_message(&eta); - prover_state.prover_message(&nu_0); - - // Phase 3c: OOD - let ood_phase = Ood::::new(); - let (ood_red, _, _) = ood_phase.prove( - prover_state, - &OodStatement { - s: self.params.config.s, - log_n, - }, - &(), - &OodProverInputs { - oracle: &tc_red_wit.f, - }, - )?; - - // Sample shift queries. - let queries = QueryIndices::::sample(prover_state, log_n, self.params.config.t); - - // Phase 3d: batching sumcheck. - let batching_phase = Batching::::new(); - let (batching_red, _, batching_red_wit) = batching_phase.prove( - prover_state, - &BatchingStatement::from_phase_outputs( - tc_red.zeta_0.clone(), - &ood_red.samples_flat, - &queries.evaluation_points, - self.params.config.s, - self.params.config.t, - log_n, - ), - &(), - &BatchingProverInputs { - oracle: &tc_red_wit.f, - }, - )?; - - // Phase 3e: proximity. - let proximity_phase = Proximity:: { - hasher: &self.params.hasher, - _phantom: PhantomData, - }; - let (_, prox, _) = proximity_phase.prove( - prover_state, - &ProximityStatement { - queries: queries.clone(), - l2, - t: self.params.config.t, - n, - }, - &(), - &ProximityProverInputs { - td_0: &pesat_red_wit.td_0, acc_td: &acc_tds, + acc_instance, }, )?; - // Assemble new accumulator state and proof. + // Assemble new accumulator state and proof from the pipeline's output. let mut nus = Vec::with_capacity(1 + self.params.config.s); - nus.push(nu_0); - nus.extend(ood_red.answers); + nus.push(reduced.nu_0); + nus.extend(reduced.ood.answers); let new_acc_instance = AccumulatorInstance { - rt: vec![td_new.root().clone()], - alpha: vec![batching_red.alpha], - mu: vec![batching_red_wit.mu], - beta: new_beta, - eta: vec![eta], + rt: vec![reduced.td_new.root().clone()], + alpha: vec![reduced.batching.alpha], + mu: vec![reduced.batching_mu], + beta: (vec![reduced.tc.beta_tau.clone()], vec![reduced.new_x]), + eta: vec![reduced.eta], }; let new_acc_witness = AccumulatorWitness { - td: vec![td_new], - w: vec![new_w], + td: vec![reduced.td_new], + w: vec![reduced.new_w], }; let proof = WARPProof { - rt_0: pesat_red_wit.td_0.root().clone(), - mu_i: pesat_red.mus, - nu_0, + rt_0: reduced.pesat_witness.td_0.root().clone(), + mu_i: reduced.pesat.mus, + nu_0: reduced.nu_0, nu_i: nus, - auth_0: prox.auth_0, - auth_j: prox.auth_j, - shift_query_answers: prox.shift_query_answers, + auth_0: reduced.proximity_proof.auth_0, + auth_j: reduced.proximity_proof.auth_j, + shift_query_answers: reduced.proximity_proof.shift_query_answers, }; Ok(((new_acc_instance, new_acc_witness), proof)) From 53a9238a9c2c8014c191d8b70880801b0514d328 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 7 May 2026 23:30:01 +0200 Subject: [PATCH 48/68] composition: generic Then combinator over Pipeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Demonstrates that the Pipeline trait actually composes — without forcing every consumer to hand-write a WarpPipeline-style struct. Design choices for this first cut: 1. Witness threading: Then::Witness = (A::Witness, B::Witness). Caller supplies a tuple, prove destructures. Simple but doesn't chain past 2 phases without nesting. Follow-up: a WitnessProjection trait so each phase plucks its slice from a flat top-level witness. 2. Context: shared. A::Context = B::Context = Then::Context. Mirrors WarpPipeline's data flow; inter-phase glue lives in one orchestrator-owned struct read by both halves. 3. Transition: a (FProve, FVerify) closure pair. Closures are ergonomic at call sites; trait-based variant is more inspectable but heavier — switch only if inference suffers. Proof shape stays nested: Then::Proof = (A::Proof, B::Proof). The planned WithFlatProof adapter (already sketched in composition.rs) flattens this to a caller-supplied type. Includes a unit test that defines a trivial fake Pipeline, composes two with Then, runs prove, and asserts the call order (A.prove → transition → B.prove), the derived B-statement (transition multiplies by 10), the final reduction, and the nested proof tuple. Not a replacement for WarpPipeline — that struct stays as the hand-tuned 5-phase WARP composition with concrete inter-phase glue (td_new commit, query sampling). Then is the generic combinator that proves the abstraction composes. --- src/protocol/mod.rs | 1 + src/protocol/then.rs | 257 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 258 insertions(+) create mode 100644 src/protocol/then.rs diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 25388ea..e44d6c4 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -2,4 +2,5 @@ pub mod composition; pub mod oracle; pub mod phases; pub mod query; +pub mod then; pub mod transcript; diff --git a/src/protocol/then.rs b/src/protocol/then.rs new file mode 100644 index 0000000..d1f185f --- /dev/null +++ b/src/protocol/then.rs @@ -0,0 +1,257 @@ +//! Generic [`Then`] combinator over [`Pipeline`]. +//! +//! Demonstrates that [`crate::protocol::composition::Pipeline`] actually +//! composes — without forcing every consumer to hand-write a +//! [`crate::protocol::composition::WarpPipeline`]-style struct each time. +//! +//! # Design choices (first cut) +//! +//! 1. **Witness threading.** `Then::Witness = (A::Witness, B::Witness)`. +//! Caller supplies a tuple, `prove` destructures. Simple, but does not +//! scale gracefully past 2 phases — chains nest as +//! `((A::W, B::W), C::W)` etc. Follow-up: introduce a +//! `WitnessProjection` trait so each phase plucks its slice from a +//! flat top-level witness, eliminating the nesting. +//! 2. **Context.** Shared. `A::Context = B::Context = Then::Context`. +//! Closer to the [`crate::protocol::composition::WarpPipeline`] data +//! flow (one orchestrator-owned struct read by all phases). Alternative +//! would be `Then::Context = (A::Context, B::Context)` — split per +//! sub-pipeline — but inter-phase glue (e.g. WARP's `eta` / `nu_0` / +//! `td_new`) is exactly the kind of thing that wants to land in shared +//! state, not in B's private context. +//! 3. **Transition function shape.** Closure rather than dedicated trait. +//! More ergonomic at call sites; the trait variant +//! (`trait Transition { fn transition(...); }`) is more +//! inspectable / nameable at the type level — switch if the closure +//! blanket impls cause inference problems for downstream users. +//! +//! # Proof shape +//! +//! `Then::Proof = (A::Proof, B::Proof)` — nests for now. A +//! `WithFlatProof` adapter (sketched in +//! [`crate::protocol::composition::WithFlatProof`]) is the planned +//! follow-up that maps the nested tuple to a flat caller-supplied proof +//! type. +//! +//! # What this file is NOT +//! +//! Not a replacement for `WarpPipeline`. `WarpPipeline` is the concrete, +//! hand-tuned five-phase composition with known WARP-specific glue +//! (committing `td_new`, sampling shift queries between Ood and +//! Batching, etc). `Then` is the generic combinator that proves the +//! abstraction composes; once verifier-side oracle handles land +//! (`crate::protocol::phases::oracle_handle`) and `Pipeline::verify` +//! is wired on `WarpPipeline`, the WARP composition can be expressed +//! as a chain of `Then` combinators if desired. + +use spongefish::{ProverState, VerifierState}; + +use crate::error::{ProverError, VerifierError}; +use crate::protocol::composition::Pipeline; + +/// Sequential composition of two [`Pipeline`]s. +/// +/// Holds `first` and `second` by value plus a `transition` value (a +/// `(prove_fn, verify_fn)` closure pair) that runs *between* the two +/// pipelines' `prove` (or `verify`) calls. The transition is where +/// orchestrator-owned glue lives — reading challenges off the +/// transcript, deriving `B`'s statement from `A`'s reduced output, +/// updating shared context. +/// +/// See module docs for the design choices behind the associated-type +/// shapes (witness tuple, shared context, closure-based transition). +pub struct Then { + pub first: A, + pub second: B, + pub transition: Trans, +} + +impl Then { + /// Build a `Then` combinator. `transition` is the inter-phase glue + /// — see the [`Pipeline`] impl below for its required shape. + pub fn new(first: A, second: B, transition: Trans) -> Self { + Self { + first, + second, + transition, + } + } +} + +/// Closure-pair-based [`Pipeline`] composition. +/// +/// `Trans` is `(FProve, FVerify)`. Most call sites only need the +/// matching half — at the time the pipeline value is constructed only +/// one side runs — but the type-level honesty of "a transition is a +/// pair of closures" matches the trait's prove/verify symmetry. +/// Callers using only one side can pass any no-op closure for the +/// other slot. +/// +/// Both closures receive: +/// - `&mut ProverState` / `&mut VerifierState` so they can sample +/// challenges or absorb glue values into the transcript, +/// - `&A::FinalReduced` — A's reduced output, +/// - `&mut Context` — the shared orchestrator-owned state, +/// +/// and return `B`'s statement. +impl Pipeline for Then +where + A: Pipeline, + B: Pipeline, + FProve: Fn(&mut ProverState, &A::FinalReduced, &mut A::Context) -> B::Statement, + FVerify: for<'a> Fn(&mut VerifierState<'a>, &A::FinalReduced, &mut A::Context) -> B::Statement, +{ + type Statement = A::Statement; + type Witness = (A::Witness, B::Witness); + type Context = A::Context; + type Proof = (A::Proof, B::Proof); + type FinalReduced = B::FinalReduced; + + fn prove( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + witness: &Self::Witness, + context: &mut Self::Context, + ) -> Result<(Self::FinalReduced, Self::Proof), ProverError> { + let (a_witness, b_witness) = witness; + let (a_reduced, a_proof) = + self.first.prove(prover_state, statement, a_witness, context)?; + let b_statement = (self.transition.0)(prover_state, &a_reduced, context); + let (b_reduced, b_proof) = + self.second + .prove(prover_state, &b_statement, b_witness, context)?; + Ok((b_reduced, (a_proof, b_proof))) + } + + fn verify<'a>( + &self, + verifier_state: &mut VerifierState<'a>, + statement: &Self::Statement, + context: &mut Self::Context, + proof: &Self::Proof, + ) -> Result { + let (a_proof, b_proof) = proof; + let a_reduced = self + .first + .verify(verifier_state, statement, context, a_proof)?; + let b_statement = (self.transition.1)(verifier_state, &a_reduced, context); + let b_reduced = self + .second + .verify(verifier_state, &b_statement, context, b_proof)?; + Ok(b_reduced) + } +} + +#[cfg(test)] +mod tests { + //! Type-level validation: the generic `Then` combinator typechecks + //! and, at runtime, runs both child pipelines in order. Crypto is + //! deliberately absent — the fake `Pipeline` implementations below + //! exist solely to drive `Then::prove` through the trait machinery. + //! + //! What we actually assert: + //! 1. `Then::prove` calls `A::prove`, then the transition closure, + //! then `B::prove`, in that order. + //! 2. The shared context is threaded through all three steps. + //! 3. `B`'s statement is derived from `A`'s reduced output (the + //! transition multiplies by 10). + //! 4. `Then::Proof = (A::Proof, B::Proof)` lands the nested tuple. + + use super::*; + + /// Test-only ordered event log. Each fake pipeline's `prove` pushes + /// an entry; the transition closure pushes its own. The final + /// vector is the assertion target. + #[derive(Default)] + struct Trace(Vec); + + /// Trivial fake pipeline — does no cryptography. `prove` records + /// its label + the witness value into the shared trace and returns + /// `stmt + wit` as the "reduction" so the transition has something + /// nontrivial to read. + struct FakePipeline { + label: &'static str, + } + + impl Pipeline for FakePipeline { + type Statement = u32; + type Witness = u32; + type Context = Trace; + type Proof = u32; + type FinalReduced = u32; + + fn prove( + &self, + _prover_state: &mut ProverState, + statement: &Self::Statement, + witness: &Self::Witness, + context: &mut Self::Context, + ) -> Result<(Self::FinalReduced, Self::Proof), ProverError> { + context.0.push(format!( + "{}.prove(stmt={}, wit={})", + self.label, statement, witness + )); + Ok((statement + witness, *witness)) + } + + fn verify<'a>( + &self, + _verifier_state: &mut VerifierState<'a>, + _statement: &Self::Statement, + _context: &mut Self::Context, + _proof: &Self::Proof, + ) -> Result { + unimplemented!("verify side not exercised by this test"); + } + } + + #[test] + fn then_runs_first_then_transition_then_second() { + let pipeline = Then::new( + FakePipeline { label: "A" }, + FakePipeline { label: "B" }, + ( + // Prove-side transition: log + derive B's stmt from A's + // reduced output. Multiplies by 10 — arbitrary + // deterministic transform we can assert on. + |_ps: &mut ProverState, a_reduced: &u32, ctx: &mut Trace| -> u32 { + ctx.0.push(format!("transition(a_reduced={})", a_reduced)); + a_reduced * 10 + }, + // Verify-side closure — never invoked in this test. + |_vs: &mut VerifierState, _r: &u32, _c: &mut Trace| -> u32 { 0 }, + ), + ); + + let mut prover_state = spongefish::domain_separator!("test::then::compose") + .without_session() + .instance(&0u32) + .std_prover(); + + let mut trace = Trace::default(); + let witness: (u32, u32) = (3, 7); + let statement: u32 = 5; + + let (final_reduced, proof) = pipeline + .prove(&mut prover_state, &statement, &witness, &mut trace) + .expect("composition should run"); + + // Order: A.prove → transition → B.prove. + assert_eq!( + trace.0.len(), + 3, + "expected 3 events; got {:?}", + trace.0 + ); + assert_eq!(trace.0[0], "A.prove(stmt=5, wit=3)"); + // A's reduced = stmt(5) + wit(3) = 8 → B's stmt = 80. + assert_eq!(trace.0[1], "transition(a_reduced=8)"); + assert_eq!(trace.0[2], "B.prove(stmt=80, wit=7)"); + + // Final reduced = B's reduced = B.stmt + B.wit = 80 + 7 = 87. + assert_eq!(final_reduced, 87); + // Proof = (A.proof, B.proof) = (A.wit, B.wit) = (3, 7). + assert_eq!(proof, (3, 7)); + } +} From b27c688b59e04d7c4fe4ef0718ce6832a2c3b5f2 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Fri, 8 May 2026 00:13:14 +0200 Subject: [PATCH 49/68] composition: add WarpPipeline.verify() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror of WarpPipeline.prove() on the verifier side. Walks the same five-phase shape — PESAT, TwinConstraint, OOD, Batching, Proximity — plus the inter-phase transcript reads (td_digest, eta, nu_0) and the shift-query / oracle-handle plumbing. Design choices: - WarpPipelineVerifyInputs takes the minimum the orchestrator must pass in: the pre-parsed AccumulatorInstance (parse_statement is orchestrator-owned because it precedes all phase work), the acc_mu_first scalar (Batching's final-claim multiplier), and a borrowed WARPProof. Other items the orchestrator caches before the call (acc_alpha_first, acc_beta_*_first) stay outside the pipeline because they're only used by the post-Batching consistency checks that cross-reference data from before the call. - WarpPipelineVerified returns the per-phase reduced statements the orchestrator still needs (pesat for taus, tc for gamma, batching for alpha) plus debug artifacts (eta, nu_0, queries, nus). Kept minimal: no field that WARP::verify doesn't touch after the inline pipeline block ends. - The arity checks on shift_query_answers.len() / auth_j.len() live in the pipeline since every phase past the OOD→Batching glue reads from the proof and assumes the shape matches. The post-Batching (alpha, beta) consistency checks stay in the orchestrator because they consume cached prior-accumulator state. WARP::verify is unchanged in this commit — switching it to call this method comes in a follow-up. --- src/protocol/composition.rs | 270 +++++++++++++++++++++++++++++++++++- 1 file changed, 269 insertions(+), 1 deletion(-) diff --git a/src/protocol/composition.rs b/src/protocol/composition.rs index ef12e79..ecf5cec 100644 --- a/src/protocol/composition.rs +++ b/src/protocol/composition.rs @@ -242,13 +242,16 @@ use crate::count_ops; use crate::crypto::merkle::{warp_scheme, WarpCommitted}; use crate::protocol::phases::batching::{ Batching, BatchingProverInputs, BatchingReducedStatement, BatchingStatement, + BatchingVerifierInputs, }; use crate::protocol::phases::ood::{Ood, OodProverInputs, OodReducedStatement, OodStatement}; +use crate::protocol::phases::oracle_handle::MerkleIndexedOracle; use crate::protocol::phases::pesat::{ Pesat, PesatReducedStatement, PesatReducedWitness, PesatStatement, PesatWitness, }; use crate::protocol::phases::proximity::{ Proximity, ProximityProofString, ProximityProverInputs, ProximityStatement, + ProximityVerifierInputs, }; use crate::protocol::phases::twin_constraint::{ TwinConstraint, TwinConstraintProverInputs, TwinConstraintReducedStatement, @@ -257,7 +260,7 @@ use crate::protocol::phases::twin_constraint::{ use crate::protocol::query::QueryIndices; use crate::relations::r1cs::R1CSConstraints; use crate::relations::BundledPESAT; -use crate::types::AccumulatorInstance; +use crate::types::{AccumulatorInstance, WARPProof}; /// Full prove-side pipeline value: Pesat → TwinConstraint → Ood → /// Batching → Proximity. @@ -558,3 +561,268 @@ where }) } } + +/// Per-call inputs for [`WarpPipeline::verify`]. Mirror of +/// [`WarpPipelineInputs`] for the verifier side. +/// +/// The orchestrator parses the transcript prefix (`parse_statement`) +/// and slices off `acc_mu_first` from the prior accumulator before +/// calling the pipeline. Everything the pipeline phases themselves +/// read from the transcript is read inside the phases — `parsed_acc` +/// supplies the public bits the TC phase needs in its +/// `Statement`, `acc_mu_first` is the Batching final-claim multiplier, +/// and the proof feeds the Proximity-side oracle handles. +pub struct WarpPipelineVerifyInputs<'a, F, H> +where + F: Field, + H: MerkleHasher>, +{ + /// Pre-parsed accumulator instance (the `l2` accumulated entries). + /// Moves into TC's `Statement`. The orchestrator owns the + /// transcript-parsing step (`parse_statement`). + pub parsed_acc: AccumulatorInstance, + /// `acc_instance.mu[0]` from the prior single-entry accumulator. + /// Feeds Batching's `acc_mu` final-claim multiplier. + pub acc_mu_first: F, + /// The proof object the verifier received. Borrowed so the + /// orchestrator keeps its own copy for any post-pipeline checks. + pub proof: &'a WARPProof, +} + +/// Output of the verify-side pipeline. +/// +/// Holds the values the orchestrator (`WARP::verify`) still needs after +/// the pipeline returns: the post-Batching consistency checks against +/// the prior accumulator's `α` and `β` are orchestrator-owned because +/// they cross-reference data outside the pipeline's scope (the +/// `acc_alpha_first` / `acc_beta_*_first` cached before the call). +/// +/// Kept minimal — only the fields `WARP::verify` reads after the +/// inline-pipeline block ends. +pub struct WarpPipelineVerified +where + F: Field, + H: MerkleHasher>, +{ + /// PESAT's reduced statement — the orchestrator reads `taus` + /// (the `l1_taus`) for the post-pipeline `β` consistency check. + pub pesat: PesatReducedStatement, + /// TwinConstraint's reduced statement — the orchestrator reads + /// `gamma` (for `gamma_eq_evals`) when assembling the expected + /// `β` from the accumulated and fresh `(τ, x)` pairs. + pub tc: TwinConstraintReducedStatement, + /// OOD's reduced statement (kept for completeness / debugging; + /// post-pipeline checks don't read it directly). + pub ood: OodReducedStatement, + /// Batching's reduced statement — the orchestrator checks + /// `acc_alpha_first == batching.alpha`. + pub batching: BatchingReducedStatement, + /// `(η, ν₀)` read off the transcript between TC and OOD. Kept so + /// callers debugging a verify failure can inspect them; not + /// required for the post-pipeline checks. + pub eta: F, + pub nu_0: F, + /// Shift query indices sampled between OOD and Batching. Kept for + /// the same reason as `eta` / `nu_0`. + pub queries: QueryIndices, + /// Recomputed `ν` vector (`1 + s + t`) — Batching's input. Kept + /// for debugging. + pub nus: Vec, + /// Phantom binding for the hasher type; the digest type appears + /// in `parsed_acc` but the verified output itself doesn't carry + /// it. + pub _phantom: PhantomData, +} + +impl<'phase, F, P, C, H> WarpPipeline<'phase, F, P, C, H> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + P: BundledPESAT, + C: LinearCode, + H: MerkleHasher>, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize + Clone + Eq, +{ + /// Run the verifier-side pipeline: + /// PESAT::verify → TC::verify → (read `td_digest, η, ν₀`, + /// discharge TC's deferred oracle check) → OOD::verify → (sample + /// shift queries, arity checks, build Merkle oracle handles) → + /// Proximity::verify → (recompute `ν` vector) → Batching::verify. + /// + /// Mirrors [`WarpPipeline::prove`] block-for-block. The + /// orchestrator handles `parse_statement` and the post-pipeline + /// `(α, β)` consistency checks against the prior accumulator — + /// those cross-reference data outside the pipeline's scope. + pub fn verify<'verifier, 'a>( + &self, + verifier_state: &mut VerifierState<'verifier>, + config: &WarpPipelineConfig, + inputs: WarpPipelineVerifyInputs<'a, F, H>, + ) -> Result, VerifierError> { + let n = self.code_len; + + // Cache the accumulated roots before `parsed_acc` moves into TC's + // statement — Proximity needs them for the per-acc Merkle handles. + let l2_roots = inputs.parsed_acc.rt.clone(); + + // ── Phase 2: PESAT ───────────────────────────────────────────── + let (pesat_red, pesat_v_out) = self.pesat.verify( + verifier_state, + &PesatStatement { + l1: config.l1, + log_m: config.log_m, + }, + &(), + )?; + let rt_0 = pesat_v_out.rt_0; + + // ── Phase 3a: twin-constraint sumcheck ───────────────────────── + let (tc_red, _) = self.twin_constraint.verify( + verifier_state, + &TwinConstraintStatement { + acc_instance: inputs.parsed_acc, + l1_mus: pesat_red.mus.clone(), + l1_taus: pesat_red.taus.clone(), + log_l: config.log_l, + log_m: config.log_m, + log_n: config.log_n, + }, + &(), + )?; + + // ── Phase 3b: TC→Ood glue (read td_digest, η, ν₀, discharge) ── + let _td_digest: H::Digest = verifier_state.prover_message()?; + let eta: F = verifier_state.prover_message()?; + let nu_0: F = verifier_state.prover_message()?; + tc_red.deferred.discharge(&tc_red.gamma, nu_0, eta)?; + + // ── Phase 3c: OOD ───────────────────────────────────────────── + let (ood_red, _) = self.ood.verify( + verifier_state, + &OodStatement { + s: config.s, + log_n: config.log_n, + }, + &(), + )?; + + // ── Phase 3c→3d glue: sample shift query indices, arity checks, + // build Merkle oracle handles ──────────────────────────────── + let n_shift_query_bytes = (config.t * config.log_n).div_ceil(8); + let bytes_shift_queries: Vec = (0..n_shift_query_bytes) + .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) + .collect(); + let queries: QueryIndices = + QueryIndices::from_squeezed_bytes(&bytes_shift_queries, config.log_n, config.t); + + // Whole-proof arity checks. The pipeline owns these — every + // phase past this point reads from `proof` and assumes the + // shape matches. + (inputs.proof.shift_query_answers.len() == config.t) + .then_some(()) + .ok_or(VerifierError::NumShiftQueries)?; + (inputs.proof.auth_j.len() == config.l2) + .then_some(()) + .ok_or(VerifierError::NumL2Instances)?; + + // Build (sorted, unique) leaf positions and a row-mapping into + // the shift_query_answers table — same construction as the + // prover side / inline `WARP::verify`. + let mut indexed: Vec<(usize, usize)> = queries + .leaf_positions + .iter() + .copied() + .enumerate() + .map(|(row, pos)| (pos, row)) + .collect(); + indexed.sort_by_key(|&(pos, _)| pos); + indexed.dedup_by_key(|&mut (pos, _)| pos); + let sorted_unique: Vec = indexed.iter().map(|&(p, _)| p).collect(); + let row_indices: Vec = indexed.iter().map(|&(_, r)| r).collect(); + + let fresh_values: Vec> = row_indices + .iter() + .map(|&r| inputs.proof.shift_query_answers[r][config.l2..].to_vec()) + .collect(); + let fresh_handle = MerkleIndexedOracle::new( + warp_scheme::(self.pesat.hasher.clone(), n), + &rt_0, + &inputs.proof.auth_0, + sorted_unique.clone(), + fresh_values, + ); + + let acc_handles: Vec> = (0..config.l2) + .map(|k| { + let acc_values: Vec> = row_indices + .iter() + .map(|&r| vec![inputs.proof.shift_query_answers[r][k]]) + .collect(); + MerkleIndexedOracle::new( + warp_scheme::(self.pesat.hasher.clone(), n), + &l2_roots[k], + &inputs.proof.auth_j[k], + sorted_unique.clone(), + acc_values, + ) + }) + .collect(); + + // ── Phase 3e: proximity (shift-query openings) ──────────────── + self.proximity.verify( + verifier_state, + &ProximityStatement { + queries: queries.clone(), + l2: config.l2, + t: config.t, + n, + }, + &ProximityVerifierInputs { + fresh: &fresh_handle, + acc: &acc_handles, + _f: PhantomData, + }, + )?; + + // ── Recompute ν vector from shift answers ──────────────────── + let gamma_eq_evals = compute_hypercube_eq_evals(config.log_l, &tc_red.gamma); + let mut nus = Vec::with_capacity(1 + config.s + config.t); + nus.push(nu_0); + nus.extend(ood_red.answers.iter().copied()); + for v_jk in inputs.proof.shift_query_answers.iter() { + let nu_st = v_jk + .iter() + .zip(&gamma_eq_evals) + .fold(F::zero(), |acc, (v, eq)| acc + *eq * *v); + nus.push(nu_st); + } + + // ── Phase 3d: batching sumcheck ────────────────────────────── + let (batching_red, _) = self.batching.verify( + verifier_state, + &BatchingStatement::from_phase_outputs( + tc_red.zeta_0.clone(), + &ood_red.samples_flat, + &queries.evaluation_points, + config.s, + config.t, + config.log_n, + ), + &BatchingVerifierInputs { + nus: nus.clone(), + acc_mu: inputs.acc_mu_first, + }, + )?; + + Ok(WarpPipelineVerified { + pesat: pesat_red, + tc: tc_red, + ood: ood_red, + batching: batching_red, + eta, + nu_0, + queries, + nus, + _phantom: PhantomData, + }) + } +} From 3526f7010d3f43bea29f136b8698af4fc2656718 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Fri, 8 May 2026 00:16:33 +0200 Subject: [PATCH 50/68] composition: replace WARP::verify body with WarpPipeline.verify() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror of the WARP::prove rewrite in 0f81cce. WARP::verify now: 1. Parses the transcript-side statement (l1 fresh + l2 acc). 2. Caches prior-accumulator values needed by post-pipeline checks (acc_alpha_first, acc_beta_*_first, acc_mu_first, l2_taus, l2_xs). 3. Builds WarpPipeline + WarpPipelineConfig. 4. Calls pipeline.verify(...) with WarpPipelineVerifyInputs { parsed_acc, acc_mu_first, proof }. 5. Runs the two post-pipeline consistency checks (alpha + beta against the prior single-entry accumulator) — these stay in the orchestrator because they cross-reference data outside the pipeline's scope. Drops imports for the per-phase types the orchestrator no longer constructs directly (Pesat/TwinConstraint/Ood/Batching/Proximity struct types, the per-phase Statement/VerifierInputs types, IOR trait, MerkleIndexedOracle, QueryIndices, warp_scheme). WARP::verify is now ~80 lines, down from ~200. The full test suite (2/2 integration + 9/9 verifier_negative + 1/1 composition_demo) passes — the verify-side pipeline produces transcript-identical output to the prior inline orchestration. --- src/lib.rs | 209 +++++++++++------------------------------------------ 1 file changed, 41 insertions(+), 168 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 38895c3..b5fb5e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,6 @@ use ark_poly::{DenseMultilinearExtension, Polynomial}; use ark_std::log2; use config::WARPConfig; use effsc::hypercube::compute_hypercube_eq_evals; -use protocol::query::QueryIndices; use protocol::transcript::{absorb_instances, parse_statement}; use relations::{r1cs::R1CSConstraints, BundledPESAT}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; @@ -48,15 +47,8 @@ pub mod prelude { use crate::crypto::merkle::warp_scheme; use error::{DeciderError, ProverError, VerifierError}; -use protocol::composition::{WarpPipeline, WarpPipelineConfig, WarpPipelineInputs}; -use protocol::phases::{ - batching::{Batching, BatchingStatement, BatchingVerifierInputs}, - ood::{Ood, OodStatement}, - oracle_handle::MerkleIndexedOracle, - pesat::{Pesat, PesatStatement}, - proximity::{Proximity, ProximityStatement, ProximityVerifierInputs}, - twin_constraint::{TwinConstraint, TwinConstraintStatement}, - IOR, +use protocol::composition::{ + WarpPipeline, WarpPipelineConfig, WarpPipelineInputs, WarpPipelineVerifyInputs, }; pub struct WARP, C: LinearCode + Clone, H: MerkleHasher> { @@ -257,179 +249,58 @@ where let n = self.params.code.code_len(); let log_n = log2(n) as usize; - // 1. Parse statement + // Parse the transcript-side statement (l1 fresh instances + l2 + // accumulated entries). Stays in the orchestrator: it crosses + // the pipeline/non-pipeline boundary by definition. let (l1_xs, parsed_acc) = parse_statement::(verifier_state, l1, l2, N - k, log_n, log_m)?; + + // Cache prior-accumulator values needed by the post-pipeline + // (α, β) consistency checks. These cross-reference the prior + // single-entry `acc_instance` against the pipeline's reduced + // outputs, so they live outside the pipeline. let acc_alpha_first = acc_instance.alpha[0].clone(); let acc_beta_0_first = acc_instance.beta.0[0].clone(); let acc_beta_1_first = acc_instance.beta.1[0].clone(); let acc_mu_first = acc_instance.mu[0]; - let l2_roots = parsed_acc.rt.clone(); let l2_taus = parsed_acc.beta.0.clone(); let l2_xs = parsed_acc.beta.1.clone(); - // 2. PESAT::verify - let pesat_phase = Pesat:: { - code: &self.params.code, - hasher: &self.params.hasher, - _phantom: PhantomData, - }; - let (pesat_red, pesat_v_out) = - pesat_phase.verify(verifier_state, &PesatStatement { l1, log_m }, &())?; - let l1_mus = pesat_red.mus; - let l1_taus = pesat_red.taus; - let rt_0 = pesat_v_out.rt_0; - - // 3. TwinConstraint::verify - let tc_phase = TwinConstraint:: { - r1cs: self.params.p.constraints(), - _phantom: PhantomData, - }; - let (tc_red, _) = tc_phase.verify( - verifier_state, - &TwinConstraintStatement { - acc_instance: parsed_acc, - l1_mus, - l1_taus: l1_taus.clone(), - log_l, - log_m, - log_n, - }, - &(), - )?; - - // 4. Read between TC and OOD: td (new commitment), η, ν₀. - let _td_digest: H::Digest = verifier_state.prover_message()?; - let eta: F = verifier_state.prover_message()?; - let nu_0: F = verifier_state.prover_message()?; - - // 5. Discharge the deferred oracle check. - tc_red.deferred.discharge(&tc_red.gamma, nu_0, eta)?; - - // 6. OOD::verify - let ood_phase = Ood::::new(); - let (ood_red, _) = ood_phase.verify( - verifier_state, - &OodStatement { - s: self.params.config.s, - log_n, - }, - &(), - )?; - - // 7. Squeeze shift-query bytes; build queries; Proximity::verify. - let n_shift_query_bytes = (self.params.config.t * log_n).div_ceil(8); - let bytes_shift_queries: Vec = (0..n_shift_query_bytes) - .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) - .collect(); - let queries: QueryIndices = - QueryIndices::from_squeezed_bytes(&bytes_shift_queries, log_n, self.params.config.t); - - // Arity checks on the proof's shape. The orchestrator owns these - // since they're whole-proof shape checks, not per-oracle opening - // validity (which lives behind the IndexedOracle handles). - (proof.shift_query_answers.len() == self.params.config.t) - .then_some(()).ok_or(VerifierError::NumShiftQueries)?; - (proof.auth_j.len() == l2).then_some(()).ok_or(VerifierError::NumL2Instances)?; - - // Build (sorted, unique) leaf positions and a map from each unique - // position to a row in shift_query_answers (duplicates land on the - // same row). - let mut indexed: Vec<(usize, usize)> = queries - .leaf_positions - .iter() - .copied() - .enumerate() - .map(|(row, pos)| (pos, row)) - .collect(); - indexed.sort_by_key(|&(pos, _)| pos); - indexed.dedup_by_key(|&mut (pos, _)| pos); - let sorted_unique: Vec = indexed.iter().map(|&(p, _)| p).collect(); - let row_indices: Vec = indexed.iter().map(|&(_, r)| r).collect(); - - // Construct the BCS-checked oracle handles. The Proximity phase - // itself stays BCS-agnostic — it only sees `&dyn IndexedOracle`. - // Each handle gets its own `WarpScheme` built from the hasher value. - let fresh_values: Vec> = row_indices - .iter() - .map(|&r| proof.shift_query_answers[r][l2..].to_vec()) - .collect(); - let fresh_handle = MerkleIndexedOracle::new( - warp_scheme::(self.params.hasher.clone(), n), - &rt_0, - &proof.auth_0, - sorted_unique.clone(), - fresh_values, + // Run the verifier-side pipeline (PESAT → TC → OOD → Proximity → Batching). + let pipeline = WarpPipeline::new( + &self.params.code, + &self.params.hasher, + self.params.p.constraints(), + &self.params.p, + n, ); - - let acc_handles: Vec> = (0..l2) - .map(|k| { - let acc_values: Vec> = row_indices - .iter() - .map(|&r| vec![proof.shift_query_answers[r][k]]) - .collect(); - MerkleIndexedOracle::new( - warp_scheme::(self.params.hasher.clone(), n), - &l2_roots[k], - &proof.auth_j[k], - sorted_unique.clone(), - acc_values, - ) - }) - .collect(); - let proximity_phase = Proximity:: { - hasher: &self.params.hasher, - _phantom: PhantomData, + let config = WarpPipelineConfig { + l1, + log_l, + log_m, + log_n, + n_minus_k: N - k, + s: self.params.config.s, + t: self.params.config.t, + l2, }; - proximity_phase.verify( + let verified = pipeline.verify( verifier_state, - &ProximityStatement { - queries: queries.clone(), - l2, - t: self.params.config.t, - n, - }, - &ProximityVerifierInputs { - fresh: &fresh_handle, - acc: &acc_handles, - _f: PhantomData, - }, - )?; - - // 8. Compute ν_{s+k} from shift answers; assemble nus. - let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &tc_red.gamma); - let mut nus = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); - nus.push(nu_0); - nus.extend(ood_red.answers); - for v_jk in proof.shift_query_answers.iter() { - let nu_st = v_jk - .iter() - .zip(&gamma_eq_evals) - .fold(F::zero(), |acc, (v, eq)| acc + *eq * *v); - nus.push(nu_st); - } - - // 9. Batching::verify. - let batching_phase = Batching::::new(); - let (batching_red, _) = batching_phase.verify( - verifier_state, - &BatchingStatement::from_phase_outputs( - tc_red.zeta_0.clone(), - &ood_red.samples_flat, - &queries.evaluation_points, - self.params.config.s, - self.params.config.t, - log_n, - ), - &BatchingVerifierInputs { - nus, - acc_mu: acc_mu_first, + &config, + WarpPipelineVerifyInputs { + parsed_acc, + acc_mu_first, + proof: &proof, }, )?; - // 10. Accumulator consistency. - (acc_alpha_first == batching_red.alpha).then_some(()).ok_or(VerifierError::CodeEvaluationPoint)?; + // Post-pipeline consistency checks against the prior accumulator. + (acc_alpha_first == verified.batching.alpha) + .then_some(()) + .ok_or(VerifierError::CodeEvaluationPoint)?; + let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &verified.tc.gamma); + let l1_taus = verified.pesat.taus; let betas = l2_taus .into_iter() .chain(l1_taus) @@ -438,7 +309,9 @@ where .collect::>>(); let beta = scale_and_sum(&betas, &gamma_eq_evals); let expected_beta = concat_slices(&acc_beta_0_first, &acc_beta_1_first); - (expected_beta == beta).then_some(()).ok_or(VerifierError::CircuitEvaluationPoint)?; + (expected_beta == beta) + .then_some(()) + .ok_or(VerifierError::CircuitEvaluationPoint)?; Ok(()) } From 0f0491148d5b1c65dc3ef22d0cbb46b66c4b8ed2 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Fri, 8 May 2026 11:13:07 +0200 Subject: [PATCH 51/68] composition: restore protocol map in WARP::prove / WARP::verify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pipeline-as-a-single-prove-call shape (commits 0f81cce / 25ac3e8) hid the protocol structure behind one opaque call. To understand what WARP does, a reader had to context-switch from lib.rs to composition.rs and reconstruct the phase ordering from the IOR machinery. This commit restores lib.rs as a self-documenting protocol map. Each phase appears as a labeled section — Phase 2 (PESAT), Phase 3a (TwinConstraint), Phase 3b (bundled η/ν₀, td_new commit, absorbs), Phase 3c (OOD), Phase 3c→3d glue (sample queries), Phase 3d (Batching), Phase 3e (Proximity) — readable top-to-bottom in order. What's preserved from the migration: - WarpPipeline still holds the five phase instances built once with their setup borrows (code, hasher, r1cs, bundled_pesat). The orchestrator calls phases via `pipeline.pesat.prove(...)` etc., so phase setup isn't duplicated across every prove() call. Pipeline serves as a *typed bag of phases* rather than an opaque executor. - WarpPipeline.prove() and .verify() stay as alternative single-call entry points used by tests/composition_demo.rs. - Pipeline trait + Then in composition.rs / then.rs survive untouched as library pieces for any future composition research. - The GAT migration (360f6dc) stays — decoupled lifetimes are a strict improvement regardless of the orchestrator shape. The two body-swap commits (0f81cce, 25ac3e8) effectively get reverted in spirit but the restoration goes through `pipeline.` not fresh phase struct construction, keeping the data-container win from the migration without paying the legibility cost. All 11 tests still pass; transcript-identical to both prior shapes. --- src/lib.rs | 338 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 279 insertions(+), 59 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b5fb5e7..d0e2062 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ use ark_poly::{DenseMultilinearExtension, Polynomial}; use ark_std::log2; use config::WARPConfig; use effsc::hypercube::compute_hypercube_eq_evals; +use protocol::query::QueryIndices; use protocol::transcript::{absorb_instances, parse_statement}; use relations::{r1cs::R1CSConstraints, BundledPESAT}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; @@ -47,8 +48,15 @@ pub mod prelude { use crate::crypto::merkle::warp_scheme; use error::{DeciderError, ProverError, VerifierError}; -use protocol::composition::{ - WarpPipeline, WarpPipelineConfig, WarpPipelineInputs, WarpPipelineVerifyInputs, +use protocol::composition::WarpPipeline; +use protocol::phases::{ + batching::{BatchingProverInputs, BatchingStatement, BatchingVerifierInputs}, + ood::{OodProverInputs, OodStatement}, + oracle_handle::MerkleIndexedOracle, + pesat::{PesatStatement, PesatWitness}, + proximity::{ProximityProverInputs, ProximityStatement, ProximityVerifierInputs}, + twin_constraint::{TwinConstraintProverInputs, TwinConstraintStatement, TwinConstraintWitness}, + IOR, }; pub struct WARP, C: LinearCode + Clone, H: MerkleHasher> { @@ -169,11 +177,12 @@ where } = acc_witness; let acc_fs: Vec> = acc_tds.iter().map(|td| td.codewords()[0].clone()).collect(); - // Build and run the full prove-side pipeline (Pesat → TwinConstraint - // → Ood → Batching → Proximity). The orchestrator's only remaining - // duty is the global before/after work: input validation, instance - // absorption, and assembling the new accumulator state plus the - // global `WARPProof` from the pipeline's reduced output. + // The pipeline holds the five phase instances built once with + // their setup borrows (code, hasher, r1cs, bundled_pesat). The + // orchestrator below drives them through the WARP protocol — + // the phase calls and inter-phase glue stay visible here as a + // protocol map. (`pipeline.prove(...)` is also available as a + // single-call entry point used by tests/composition_demo.rs.) let pipeline = WarpPipeline::new( &self.params.code, &self.params.hasher, @@ -181,53 +190,137 @@ where &self.params.p, n, ); - let config = WarpPipelineConfig { - l1, - log_l, - log_m, - log_n, - n_minus_k: N - k, - s: self.params.config.s, - t: self.params.config.t, - l2, - }; - let reduced = pipeline.prove( + + // ── Phase 2: PESAT — encode witnesses, commit, derive τ ────── + let (pesat_red, _pesat_proof, pesat_red_wit) = pipeline.pesat.prove( prover_state, - &config, - WarpPipelineInputs { + &PesatStatement { l1, log_m }, + &PesatWitness { witnesses: &witnesses, - instances: &instances, + }, + &(), + )?; + + // ── Phase 3a: twin-constraint sumcheck ────────────────────── + let (tc_red, _tc_proof, tc_red_wit) = pipeline.twin_constraint.prove( + prover_state, + &TwinConstraintStatement { + acc_instance, + l1_mus: pesat_red.mus.clone(), + l1_taus: pesat_red.taus.clone(), + log_l, + log_m, + log_n, + }, + &TwinConstraintWitness { acc_witness_w: &acc_ws, + instances: &instances, + witnesses: &witnesses, + }, + &TwinConstraintProverInputs { + fresh_codewords: &pesat_red_wit.codewords, acc_codewords: &acc_fs, + }, + )?; + + // ── Phase 3b: bundled η, ν₀, new commitment, absorb ────────── + let beta_eq_evals = compute_hypercube_eq_evals(log_m, &tc_red.beta_tau); + let eta = self + .params + .p + .evaluate_bundled(&beta_eq_evals, &tc_red_wit.z) + .map_err(|_| ProverError::SpongeFish)?; + let nu_0 = tc_red_wit.f.query_at_point(&tc_red.zeta_0); + + let (new_x, new_w) = tc_red_wit.z.split_at(N - k); + let new_x = new_x.to_vec(); + let new_w = new_w.to_vec(); + let new_beta = (vec![tc_red.beta_tau.clone()], vec![new_x]); + + let td_new = { + let _s = tracing::info_span!("warp.commit_new_oracle").entered(); + count_ops!(MerkleTreeBuilds); + let scheme = warp_scheme::(self.params.hasher.clone(), n); + scheme.commit(&[tc_red_wit.f.evals().to_vec()]) + }; + prover_state.prover_message(td_new.root()); + prover_state.prover_message(&eta); + prover_state.prover_message(&nu_0); + + // ── Phase 3c: OOD ────────────────────────────────────────── + let (ood_red, _, _) = pipeline.ood.prove( + prover_state, + &OodStatement { + s: self.params.config.s, + log_n, + }, + &(), + &OodProverInputs { + oracle: &tc_red_wit.f, + }, + )?; + + // ── Phase 3c → 3d glue: sample shift queries ─────────────── + let queries = QueryIndices::::sample(prover_state, log_n, self.params.config.t); + + // ── Phase 3d: batching sumcheck (DAG fan-in: tc.zeta_0 + + // ood.samples_flat + queries.evaluation_points) ───────────── + let (batching_red, _, batching_red_wit) = pipeline.batching.prove( + prover_state, + &BatchingStatement::from_phase_outputs( + tc_red.zeta_0.clone(), + &ood_red.samples_flat, + &queries.evaluation_points, + self.params.config.s, + self.params.config.t, + log_n, + ), + &(), + &BatchingProverInputs { + oracle: &tc_red_wit.f, + }, + )?; + + // ── Phase 3e: proximity (shift-query openings) ───────────── + let (_, prox, _) = pipeline.proximity.prove( + prover_state, + &ProximityStatement { + queries: queries.clone(), + l2, + t: self.params.config.t, + n, + }, + &(), + &ProximityProverInputs { + td_0: &pesat_red_wit.td_0, acc_td: &acc_tds, - acc_instance, }, )?; - // Assemble new accumulator state and proof from the pipeline's output. + // ── Assemble new accumulator state and proof ─────────────── let mut nus = Vec::with_capacity(1 + self.params.config.s); - nus.push(reduced.nu_0); - nus.extend(reduced.ood.answers); + nus.push(nu_0); + nus.extend(ood_red.answers); let new_acc_instance = AccumulatorInstance { - rt: vec![reduced.td_new.root().clone()], - alpha: vec![reduced.batching.alpha], - mu: vec![reduced.batching_mu], - beta: (vec![reduced.tc.beta_tau.clone()], vec![reduced.new_x]), - eta: vec![reduced.eta], + rt: vec![td_new.root().clone()], + alpha: vec![batching_red.alpha], + mu: vec![batching_red_wit.mu], + beta: new_beta, + eta: vec![eta], }; let new_acc_witness = AccumulatorWitness { - td: vec![reduced.td_new], - w: vec![reduced.new_w], + td: vec![td_new], + w: vec![new_w], }; let proof = WARPProof { - rt_0: reduced.pesat_witness.td_0.root().clone(), - mu_i: reduced.pesat.mus, - nu_0: reduced.nu_0, + rt_0: pesat_red_wit.td_0.root().clone(), + mu_i: pesat_red.mus, + nu_0, nu_i: nus, - auth_0: reduced.proximity_proof.auth_0, - auth_j: reduced.proximity_proof.auth_j, - shift_query_answers: reduced.proximity_proof.shift_query_answers, + auth_0: prox.auth_0, + auth_j: prox.auth_j, + shift_query_answers: prox.shift_query_answers, }; Ok(((new_acc_instance, new_acc_witness), proof)) @@ -266,7 +359,13 @@ where let l2_taus = parsed_acc.beta.0.clone(); let l2_xs = parsed_acc.beta.1.clone(); - // Run the verifier-side pipeline (PESAT → TC → OOD → Proximity → Batching). + // Cache the accumulated roots before `parsed_acc` moves into TC's + // statement — Proximity needs them for the per-acc Merkle handles. + let l2_roots = parsed_acc.rt.clone(); + + // Pipeline holds the five phase instances. The orchestrator drives + // them through the WARP verifier protocol below; phase calls and + // inter-phase glue stay visible here. let pipeline = WarpPipeline::new( &self.params.code, &self.params.hasher, @@ -274,33 +373,154 @@ where &self.params.p, n, ); - let config = WarpPipelineConfig { - l1, - log_l, - log_m, - log_n, - n_minus_k: N - k, - s: self.params.config.s, - t: self.params.config.t, - l2, - }; - let verified = pipeline.verify( + + // ── Phase 2: PESAT::verify ────────────────────────────────── + let (pesat_red, pesat_v_out) = pipeline.pesat.verify( + verifier_state, + &PesatStatement { l1, log_m }, + &(), + )?; + let l1_mus = pesat_red.mus; + let l1_taus = pesat_red.taus; + let rt_0 = pesat_v_out.rt_0; + + // ── Phase 3a: TwinConstraint::verify ──────────────────────── + let (tc_red, _) = pipeline.twin_constraint.verify( + verifier_state, + &TwinConstraintStatement { + acc_instance: parsed_acc, + l1_mus, + l1_taus: l1_taus.clone(), + log_l, + log_m, + log_n, + }, + &(), + )?; + + // ── Phase 3b: read td_digest, η, ν₀; discharge deferred check + let _td_digest: H::Digest = verifier_state.prover_message()?; + let eta: F = verifier_state.prover_message()?; + let nu_0: F = verifier_state.prover_message()?; + tc_red.deferred.discharge(&tc_red.gamma, nu_0, eta)?; + + // ── Phase 3c: OOD::verify ─────────────────────────────────── + let (ood_red, _) = pipeline.ood.verify( + verifier_state, + &OodStatement { + s: self.params.config.s, + log_n, + }, + &(), + )?; + + // ── Phase 3c → 3d glue: sample shift queries, arity checks, + // build IndexedOracle handles for fresh + accumulated commits + let n_shift_query_bytes = (self.params.config.t * log_n).div_ceil(8); + let bytes_shift_queries: Vec = (0..n_shift_query_bytes) + .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) + .collect(); + let queries: QueryIndices = + QueryIndices::from_squeezed_bytes(&bytes_shift_queries, log_n, self.params.config.t); + + (proof.shift_query_answers.len() == self.params.config.t) + .then_some(()) + .ok_or(VerifierError::NumShiftQueries)?; + (proof.auth_j.len() == l2) + .then_some(()) + .ok_or(VerifierError::NumL2Instances)?; + + let mut indexed: Vec<(usize, usize)> = queries + .leaf_positions + .iter() + .copied() + .enumerate() + .map(|(row, pos)| (pos, row)) + .collect(); + indexed.sort_by_key(|&(pos, _)| pos); + indexed.dedup_by_key(|&mut (pos, _)| pos); + let sorted_unique: Vec = indexed.iter().map(|&(p, _)| p).collect(); + let row_indices: Vec = indexed.iter().map(|&(_, r)| r).collect(); + + let fresh_values: Vec> = row_indices + .iter() + .map(|&r| proof.shift_query_answers[r][l2..].to_vec()) + .collect(); + let fresh_handle = MerkleIndexedOracle::new( + warp_scheme::(self.params.hasher.clone(), n), + &rt_0, + &proof.auth_0, + sorted_unique.clone(), + fresh_values, + ); + + let acc_handles: Vec> = (0..l2) + .map(|j| { + let acc_values: Vec> = row_indices + .iter() + .map(|&r| vec![proof.shift_query_answers[r][j]]) + .collect(); + MerkleIndexedOracle::new( + warp_scheme::(self.params.hasher.clone(), n), + &l2_roots[j], + &proof.auth_j[j], + sorted_unique.clone(), + acc_values, + ) + }) + .collect(); + + // ── Phase 3e: Proximity::verify ───────────────────────────── + pipeline.proximity.verify( + verifier_state, + &ProximityStatement { + queries: queries.clone(), + l2, + t: self.params.config.t, + n, + }, + &ProximityVerifierInputs { + fresh: &fresh_handle, + acc: &acc_handles, + _f: PhantomData, + }, + )?; + + // ── Recompute ν vector from shift answers ────────────────── + let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &tc_red.gamma); + let mut nus = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); + nus.push(nu_0); + nus.extend(ood_red.answers); + for v_jk in proof.shift_query_answers.iter() { + let nu_st = v_jk + .iter() + .zip(&gamma_eq_evals) + .fold(F::zero(), |acc, (v, eq)| acc + *eq * *v); + nus.push(nu_st); + } + + // ── Phase 3d: Batching::verify ───────────────────────────── + let (batching_red, _) = pipeline.batching.verify( verifier_state, - &config, - WarpPipelineVerifyInputs { - parsed_acc, - acc_mu_first, - proof: &proof, + &BatchingStatement::from_phase_outputs( + tc_red.zeta_0.clone(), + &ood_red.samples_flat, + &queries.evaluation_points, + self.params.config.s, + self.params.config.t, + log_n, + ), + &BatchingVerifierInputs { + nus, + acc_mu: acc_mu_first, }, )?; - // Post-pipeline consistency checks against the prior accumulator. - (acc_alpha_first == verified.batching.alpha) + // ── Post-pipeline accumulator consistency ────────────────── + (acc_alpha_first == batching_red.alpha) .then_some(()) .ok_or(VerifierError::CodeEvaluationPoint)?; - let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &verified.tc.gamma); - let l1_taus = verified.pesat.taus; let betas = l2_taus .into_iter() .chain(l1_taus) From 1e4edcd1f540eb480bcca370d74da0fcf55881af Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 12 May 2026 11:02:32 +0200 Subject: [PATCH 52/68] choreography: IOR-uniform protocol map with destructured outputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reshape the orchestrator around two principles surfaced in design discussion: 1. **Every step is an IOR**, including the inter-phase glue. The bridge between TwinConstraint and OOD (η / ν₀ / td_new commit, deferred discharge) and the shift-query sampling between OOD and Batching were previously inline orchestrator code; both are now first-class IORs in `src/protocol/phases/{bridge.rs, sample_queries.rs}` with the full IOR-trait signature, even where the Statement/Witness fields are degenerate. Uniformity: one abstraction, used everywhere. 2. **Destructure on every IOR output.** Instead of opaque step-wrapper types hiding the protocol values, the choreography in `WARP::prove` / `WARP::verify` destructures each `ReducedStatement` and `ReducedWitness` by name at the call site: let (PesatReducedStatement { mus, taus }, _, PesatReducedWitness { codewords, td_0 }) = pesat_phase.prove(...)?; The reader sees, on each phase line: which IOR runs, what flows in (named struct fields), what flows out (named bindings). Paper-aligned notation visible throughout the orchestrator. Bridge IOR (`src/protocol/phases/bridge.rs`): - Statement: (ζ₀, β_τ, log_m, n_minus_k) - Witness: (z, f) — prover's reduced state from TC - ProverInputs: (bundled_pesat, hasher, code_len) - VerifierInputs: (deferred, γ) — for discharging TC's deferred check - ReductionInputs/ReducedStatement: (η, ν₀, td_new_root) - ReducedWitness: (td_new, new_x, new_w) - Prover-side: computes η, ν₀, splits z, commits td_new, absorbs. - Verifier-side: reads td_digest/η/ν₀, discharges deferred check. SampleQueries IOR (`src/protocol/phases/sample_queries.rs`): - Statement: (log_n, t) - ReductionInputs/ReducedStatement: QueryIndices - Trivially: one round of verifier randomness, no prover messages. Deletes the pipeline machinery the choreography subsumes: - `src/protocol/composition.rs` (WarpPipeline + Pipeline trait sketch) - `src/protocol/then.rs` (generic Then) - `tests/composition_demo.rs` (exercised WarpPipeline directly) `WARP::prove` and `WARP::verify` now construct phase structs inline (stateless or borrowing &self.params data, so no setup repeated work). Same transcript-identical output as before; all 33 tests (22 lib + 2 integration + 9 verifier-negative) pass. --- src/lib.rs | 344 +++++++---- src/protocol/composition.rs | 828 -------------------------- src/protocol/mod.rs | 2 - src/protocol/phases/bridge.rs | 271 +++++++++ src/protocol/phases/mod.rs | 2 + src/protocol/phases/sample_queries.rs | 135 +++++ src/protocol/then.rs | 257 -------- tests/composition_demo.rs | 187 ------ 8 files changed, 621 insertions(+), 1405 deletions(-) delete mode 100644 src/protocol/composition.rs create mode 100644 src/protocol/phases/bridge.rs create mode 100644 src/protocol/phases/sample_queries.rs delete mode 100644 src/protocol/then.rs delete mode 100644 tests/composition_demo.rs diff --git a/src/lib.rs b/src/lib.rs index d0e2062..1670530 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,6 @@ use ark_poly::{DenseMultilinearExtension, Polynomial}; use ark_std::log2; use config::WARPConfig; use effsc::hypercube::compute_hypercube_eq_evals; -use protocol::query::QueryIndices; use protocol::transcript::{absorb_instances, parse_statement}; use relations::{r1cs::R1CSConstraints, BundledPESAT}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; @@ -48,14 +47,30 @@ pub mod prelude { use crate::crypto::merkle::warp_scheme; use error::{DeciderError, ProverError, VerifierError}; -use protocol::composition::WarpPipeline; use protocol::phases::{ - batching::{BatchingProverInputs, BatchingStatement, BatchingVerifierInputs}, - ood::{OodProverInputs, OodStatement}, + batching::{ + Batching, BatchingProverInputs, BatchingReducedStatement, BatchingReducedWitness, + BatchingStatement, BatchingVerifierInputs, + }, + bridge::{ + Bridge, BridgeProverInputs, BridgeReducedStatement, BridgeReducedWitness, BridgeStatement, + BridgeVerifierInputs, BridgeWitness, + }, + ood::{Ood, OodProverInputs, OodReducedStatement, OodStatement}, oracle_handle::MerkleIndexedOracle, - pesat::{PesatStatement, PesatWitness}, - proximity::{ProximityProverInputs, ProximityStatement, ProximityVerifierInputs}, - twin_constraint::{TwinConstraintProverInputs, TwinConstraintStatement, TwinConstraintWitness}, + pesat::{ + Pesat, PesatReducedStatement, PesatReducedWitness, PesatStatement, PesatVerifierOutputs, + PesatWitness, + }, + proximity::{ + Proximity, ProximityProofString, ProximityProverInputs, ProximityStatement, + ProximityVerifierInputs, + }, + sample_queries::{SampleQueries, SampleQueriesReducedStatement, SampleQueriesStatement}, + twin_constraint::{ + TwinConstraint, TwinConstraintProverInputs, TwinConstraintReducedStatement, + TwinConstraintReducedWitness, TwinConstraintStatement, TwinConstraintWitness, + }, IOR, }; @@ -177,37 +192,52 @@ where } = acc_witness; let acc_fs: Vec> = acc_tds.iter().map(|td| td.codewords()[0].clone()).collect(); - // The pipeline holds the five phase instances built once with - // their setup borrows (code, hasher, r1cs, bundled_pesat). The - // orchestrator below drives them through the WARP protocol — - // the phase calls and inter-phase glue stay visible here as a - // protocol map. (`pipeline.prove(...)` is also available as a - // single-call entry point used by tests/composition_demo.rs.) - let pipeline = WarpPipeline::new( - &self.params.code, - &self.params.hasher, - self.params.p.constraints(), - &self.params.p, - n, - ); + // Phase structs (held by value, borrow setup data for &self lifetime). + let pesat_phase = Pesat:: { + code: &self.params.code, + hasher: &self.params.hasher, + _phantom: PhantomData, + }; + let tc_phase = TwinConstraint:: { + r1cs: self.params.p.constraints(), + _phantom: PhantomData, + }; + let bridge_phase = Bridge::::new(); + let ood_phase = Ood::::new(); + let sample_queries_phase = SampleQueries::::new(); + let batching_phase = Batching::::new(); + let proximity_phase = Proximity:: { + hasher: &self.params.hasher, + _phantom: PhantomData, + }; // ── Phase 2: PESAT — encode witnesses, commit, derive τ ────── - let (pesat_red, _pesat_proof, pesat_red_wit) = pipeline.pesat.prove( - prover_state, - &PesatStatement { l1, log_m }, - &PesatWitness { - witnesses: &witnesses, + let (PesatReducedStatement { mus, taus }, _, PesatReducedWitness { codewords, td_0 }) = + pesat_phase.prove( + prover_state, + &PesatStatement { l1, log_m }, + &PesatWitness { + witnesses: &witnesses, + }, + &(), + )?; + + // ── Phase 3a: TwinConstraint — fused-fold sumcheck ─────────── + let ( + TwinConstraintReducedStatement { + gamma: _, + zeta_0, + beta_tau, + deferred: _, }, - &(), - )?; - - // ── Phase 3a: twin-constraint sumcheck ────────────────────── - let (tc_red, _tc_proof, tc_red_wit) = pipeline.twin_constraint.prove( + _, + TwinConstraintReducedWitness { f, z }, + ) = tc_phase.prove( prover_state, &TwinConstraintStatement { acc_instance, - l1_mus: pesat_red.mus.clone(), - l1_taus: pesat_red.taus.clone(), + l1_mus: mus.clone(), + l1_taus: taus, log_l, log_m, log_n, @@ -218,71 +248,89 @@ where witnesses: &witnesses, }, &TwinConstraintProverInputs { - fresh_codewords: &pesat_red_wit.codewords, + fresh_codewords: &codewords, acc_codewords: &acc_fs, }, )?; - // ── Phase 3b: bundled η, ν₀, new commitment, absorb ────────── - let beta_eq_evals = compute_hypercube_eq_evals(log_m, &tc_red.beta_tau); - let eta = self - .params - .p - .evaluate_bundled(&beta_eq_evals, &tc_red_wit.z) - .map_err(|_| ProverError::SpongeFish)?; - let nu_0 = tc_red_wit.f.query_at_point(&tc_red.zeta_0); - - let (new_x, new_w) = tc_red_wit.z.split_at(N - k); - let new_x = new_x.to_vec(); - let new_w = new_w.to_vec(); - let new_beta = (vec![tc_red.beta_tau.clone()], vec![new_x]); - - let td_new = { - let _s = tracing::info_span!("warp.commit_new_oracle").entered(); - count_ops!(MerkleTreeBuilds); - let scheme = warp_scheme::(self.params.hasher.clone(), n); - scheme.commit(&[tc_red_wit.f.evals().to_vec()]) - }; - prover_state.prover_message(td_new.root()); - prover_state.prover_message(&eta); - prover_state.prover_message(&nu_0); + // ── Phase 3b: Bridge — publish (td_new, η, ν₀); split z ────── + let ( + BridgeReducedStatement { + eta, + nu_0, + td_new_root: _, + }, + _, + BridgeReducedWitness { + td_new, + new_x, + new_w, + }, + ) = bridge_phase.prove( + prover_state, + &BridgeStatement { + zeta_0: zeta_0.clone(), + beta_tau: beta_tau.clone(), + log_m, + n_minus_k: N - k, + }, + &BridgeWitness { z: &z, f: &f }, + &BridgeProverInputs { + bundled_pesat: &self.params.p, + hasher: &self.params.hasher, + code_len: n, + _f: PhantomData, + }, + )?; - // ── Phase 3c: OOD ────────────────────────────────────────── - let (ood_red, _, _) = pipeline.ood.prove( + // ── Phase 3c: OOD — out-of-domain queries ──────────────────── + let (OodReducedStatement { samples_flat, answers }, _, _) = ood_phase.prove( prover_state, &OodStatement { s: self.params.config.s, log_n, }, &(), - &OodProverInputs { - oracle: &tc_red_wit.f, - }, + &OodProverInputs { oracle: &f }, )?; - // ── Phase 3c → 3d glue: sample shift queries ─────────────── - let queries = QueryIndices::::sample(prover_state, log_n, self.params.config.t); - - // ── Phase 3d: batching sumcheck (DAG fan-in: tc.zeta_0 + - // ood.samples_flat + queries.evaluation_points) ───────────── - let (batching_red, _, batching_red_wit) = pipeline.batching.prove( + // ── Phase 3c → 3d: SampleQueries — sample t shift positions ── + let (SampleQueriesReducedStatement { queries }, _, _) = sample_queries_phase.prove( prover_state, - &BatchingStatement::from_phase_outputs( - tc_red.zeta_0.clone(), - &ood_red.samples_flat, - &queries.evaluation_points, - self.params.config.s, - self.params.config.t, + &SampleQueriesStatement { log_n, - ), - &(), - &BatchingProverInputs { - oracle: &tc_red_wit.f, + t: self.params.config.t, }, + &(), + &(), )?; - // ── Phase 3e: proximity (shift-query openings) ───────────── - let (_, prox, _) = pipeline.proximity.prove( + // ── Phase 3d: Batching — DAG fan-in over (ζ₀, OOD, queries) ── + let (BatchingReducedStatement { alpha }, _, BatchingReducedWitness { mu }) = batching_phase + .prove( + prover_state, + &BatchingStatement::from_phase_outputs( + zeta_0.clone(), + &samples_flat, + &queries.evaluation_points, + self.params.config.s, + self.params.config.t, + log_n, + ), + &(), + &BatchingProverInputs { oracle: &f }, + )?; + + // ── Phase 3e: Proximity — shift-query openings ─────────────── + let ( + _, + ProximityProofString { + auth_0, + auth_j, + shift_query_answers, + }, + _, + ) = proximity_phase.prove( prover_state, &ProximityStatement { queries: queries.clone(), @@ -292,21 +340,21 @@ where }, &(), &ProximityProverInputs { - td_0: &pesat_red_wit.td_0, + td_0: &td_0, acc_td: &acc_tds, }, )?; - // ── Assemble new accumulator state and proof ─────────────── + // ── Assemble new accumulator state and proof ───────────────── let mut nus = Vec::with_capacity(1 + self.params.config.s); nus.push(nu_0); - nus.extend(ood_red.answers); + nus.extend(answers); let new_acc_instance = AccumulatorInstance { rt: vec![td_new.root().clone()], - alpha: vec![batching_red.alpha], - mu: vec![batching_red_wit.mu], - beta: new_beta, + alpha: vec![alpha], + mu: vec![mu], + beta: (vec![beta_tau], vec![new_x]), eta: vec![eta], }; let new_acc_witness = AccumulatorWitness { @@ -314,13 +362,13 @@ where w: vec![new_w], }; let proof = WARPProof { - rt_0: pesat_red_wit.td_0.root().clone(), - mu_i: pesat_red.mus, + rt_0: td_0.root().clone(), + mu_i: mus, nu_0, nu_i: nus, - auth_0: prox.auth_0, - auth_j: prox.auth_j, - shift_query_answers: prox.shift_query_answers, + auth_0, + auth_j, + shift_query_answers, }; Ok(((new_acc_instance, new_acc_witness), proof)) @@ -363,33 +411,48 @@ where // statement — Proximity needs them for the per-acc Merkle handles. let l2_roots = parsed_acc.rt.clone(); - // Pipeline holds the five phase instances. The orchestrator drives - // them through the WARP verifier protocol below; phase calls and - // inter-phase glue stay visible here. - let pipeline = WarpPipeline::new( - &self.params.code, - &self.params.hasher, - self.params.p.constraints(), - &self.params.p, - n, - ); + // Phase structs (held by value, borrow setup data for &self lifetime). + let pesat_phase = Pesat:: { + code: &self.params.code, + hasher: &self.params.hasher, + _phantom: PhantomData, + }; + let tc_phase = TwinConstraint:: { + r1cs: self.params.p.constraints(), + _phantom: PhantomData, + }; + let bridge_phase = Bridge::::new(); + let ood_phase = Ood::::new(); + let sample_queries_phase = SampleQueries::::new(); + let batching_phase = Batching::::new(); + let proximity_phase = Proximity:: { + hasher: &self.params.hasher, + _phantom: PhantomData, + }; // ── Phase 2: PESAT::verify ────────────────────────────────── - let (pesat_red, pesat_v_out) = pipeline.pesat.verify( - verifier_state, - &PesatStatement { l1, log_m }, - &(), - )?; - let l1_mus = pesat_red.mus; - let l1_taus = pesat_red.taus; - let rt_0 = pesat_v_out.rt_0; + let ( + PesatReducedStatement { + mus: l1_mus, + taus: l1_taus, + }, + PesatVerifierOutputs { rt_0 }, + ) = pesat_phase.verify(verifier_state, &PesatStatement { l1, log_m }, &())?; // ── Phase 3a: TwinConstraint::verify ──────────────────────── - let (tc_red, _) = pipeline.twin_constraint.verify( + let ( + TwinConstraintReducedStatement { + gamma, + zeta_0, + beta_tau, + deferred, + }, + _, + ) = tc_phase.verify( verifier_state, &TwinConstraintStatement { acc_instance: parsed_acc, - l1_mus, + l1_mus: l1_mus.clone(), l1_taus: l1_taus.clone(), log_l, log_m, @@ -398,14 +461,30 @@ where &(), )?; - // ── Phase 3b: read td_digest, η, ν₀; discharge deferred check - let _td_digest: H::Digest = verifier_state.prover_message()?; - let eta: F = verifier_state.prover_message()?; - let nu_0: F = verifier_state.prover_message()?; - tc_red.deferred.discharge(&tc_red.gamma, nu_0, eta)?; + // ── Phase 3b: Bridge::verify — read (td, η, ν₀), discharge TC's deferred check ─ + let ( + BridgeReducedStatement { + eta: _, + nu_0, + td_new_root: _, + }, + _, + ) = bridge_phase.verify( + verifier_state, + &BridgeStatement { + zeta_0: zeta_0.clone(), + beta_tau, + log_m, + n_minus_k: N - k, + }, + &BridgeVerifierInputs { + deferred: &deferred, + gamma: &gamma, + }, + )?; // ── Phase 3c: OOD::verify ─────────────────────────────────── - let (ood_red, _) = pipeline.ood.verify( + let (OodReducedStatement { samples_flat, answers }, _) = ood_phase.verify( verifier_state, &OodStatement { s: self.params.config.s, @@ -414,15 +493,17 @@ where &(), )?; - // ── Phase 3c → 3d glue: sample shift queries, arity checks, - // build IndexedOracle handles for fresh + accumulated commits - let n_shift_query_bytes = (self.params.config.t * log_n).div_ceil(8); - let bytes_shift_queries: Vec = (0..n_shift_query_bytes) - .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) - .collect(); - let queries: QueryIndices = - QueryIndices::from_squeezed_bytes(&bytes_shift_queries, log_n, self.params.config.t); + // ── Phase 3c → 3d: SampleQueries::verify ──────────────────── + let (SampleQueriesReducedStatement { queries }, _) = sample_queries_phase.verify( + verifier_state, + &SampleQueriesStatement { + log_n, + t: self.params.config.t, + }, + &(), + )?; + // ── Whole-proof arity checks (orchestrator-owned) ──────────── (proof.shift_query_answers.len() == self.params.config.t) .then_some(()) .ok_or(VerifierError::NumShiftQueries)?; @@ -430,6 +511,7 @@ where .then_some(()) .ok_or(VerifierError::NumL2Instances)?; + // ── Build IndexedOracle handles for Proximity ─────────────── let mut indexed: Vec<(usize, usize)> = queries .leaf_positions .iter() @@ -471,7 +553,7 @@ where .collect(); // ── Phase 3e: Proximity::verify ───────────────────────────── - pipeline.proximity.verify( + proximity_phase.verify( verifier_state, &ProximityStatement { queries: queries.clone(), @@ -487,10 +569,10 @@ where )?; // ── Recompute ν vector from shift answers ────────────────── - let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &tc_red.gamma); + let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma); let mut nus = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); nus.push(nu_0); - nus.extend(ood_red.answers); + nus.extend(answers); for v_jk in proof.shift_query_answers.iter() { let nu_st = v_jk .iter() @@ -500,11 +582,11 @@ where } // ── Phase 3d: Batching::verify ───────────────────────────── - let (batching_red, _) = pipeline.batching.verify( + let (BatchingReducedStatement { alpha }, _) = batching_phase.verify( verifier_state, &BatchingStatement::from_phase_outputs( - tc_red.zeta_0.clone(), - &ood_red.samples_flat, + zeta_0.clone(), + &samples_flat, &queries.evaluation_points, self.params.config.s, self.params.config.t, @@ -517,7 +599,7 @@ where )?; // ── Post-pipeline accumulator consistency ────────────────── - (acc_alpha_first == batching_red.alpha) + (acc_alpha_first == alpha) .then_some(()) .ok_or(VerifierError::CodeEvaluationPoint)?; diff --git a/src/protocol/composition.rs b/src/protocol/composition.rs deleted file mode 100644 index ecf5cec..0000000 --- a/src/protocol/composition.rs +++ /dev/null @@ -1,828 +0,0 @@ -//! Typed composition of [`IOR`]s — design sketch. -//! -//! # Status -//! -//! **Sketch, not yet wired.** The orchestrator in [`crate::WARP::prove`] / -//! [`crate::WARP::verify`] still threads the five phases by hand. This -//! module proposes a layered design — keep [`IOR`] as the single-phase -//! abstraction it already is, build a new [`Pipeline`] trait above it -//! for composition. -//! -//! # Why two traits, not one extended one -//! -//! `IOR` is well-shaped for one phase: `(stmt, wit, oracles_in) → -//! (reduced_stmt, oracles_out)` plus the [`IOR::reduce_statement`] single -//! source of truth. Trying to make a `Then` value `impl IOR` runs -//! into three problems: -//! -//! - `ReductionInputs` doesn't generalize. It's per-phase by construction -//! — a composition reduces a *sequence* of statements, not one. -//! - `ProofString = (A::ProofString, B::ProofString)` nests 5-deep for -//! WARP. Extracting pieces to assemble the global proof is just the -//! hand-threading we wanted to eliminate, in a different shape. -//! - WARP's data flow isn't a clean linear chain — it's DAG-shaped. -//! `BatchingStatement` reads from TwinConstraint's reduction *and* -//! Ood's outputs *and* orchestrator-sampled queries. `IOR` has no -//! place for "between-phase orchestrator work." -//! -//! Analogy: `Functor` captures `fmap`, `Monad` captures sequencing — -//! they're separate traits because they're different concepts. Same here: -//! `IOR` captures phase semantics; `Pipeline` captures composition. -//! Folding sequencing into `IOR` makes both worse. -//! -//! # The shape -//! -//! ```ignore -//! trait Pipeline { -//! type Statement; -//! type Witness; -//! type Context; // orchestrator-owned state read by phases -//! type Proof; // flat — pipeline owns the assembly -//! type FinalReduced; // the last phase's ReducedStatement -//! fn prove(...) -> Result<(Self::FinalReduced, Self::Proof), ProverError>; -//! fn verify(...) -> Result; -//! } -//! ``` -//! -//! Three things make this work where the bare `Then.impl IOR` attempt -//! didn't: -//! -//! 1. **Context as a first-class associated type.** WARP's actual data -//! flow has each phase reading some upstream-phase output *and* some -//! pipeline-level shared state (the `acc_instance`, the fresh -//! witnesses, the sampled queries, etc.). Context is that shared -//! state. The orchestrator builds it once; each phase reads what it -//! needs. -//! 2. **Witness projection.** Each phase reads a different slice of the -//! pipeline's `Witness`. A composition combinator takes a -//! user-supplied projection `Self::Witness → SubPhase::Witness` so -//! no re-cloning is forced. -//! 3. **Flat `Proof`.** The pipeline owns proof assembly — a leaf -//! pipeline produces a concrete user-defined proof type via a -//! user-supplied `assemble` function. No nested-tuple -//! `(((A, B), C), D)` shapes leak out. -//! -//! # Worked example: how WARP would compose -//! -//! ```ignore -//! // Context = everything the orchestrator computes / holds across phases. -//! struct WARPContext<'a, F, H> { -//! acc_instance: &'a AccumulatorInstance, -//! acc_witness: &'a AccumulatorWitness, -//! acc_codewords: &'a [Vec], -//! instances: &'a [Vec], -//! witnesses: &'a [Vec], -//! queries: Option>, // populated mid-pipeline -//! pesat_outputs: Option>, // populated by Pesat phase -//! // ... etc. -//! } -//! -//! let pipeline = -//! Pesat::new(...) -//! .lift(/* prover_inputs_fn */ |_, ctx| (), /* witness_fn */ |w, _| w.fresh) -//! .then( -//! TwinConstraint::new(...).lift(...), -//! /* make_next_stmt */ |pesat_red, ctx| TwinConstraintStatement { -//! acc_instance: ctx.acc_instance.clone(), -//! l1_mus: pesat_red.mus.clone(), -//! l1_taus: pesat_red.taus.clone(), -//! .. -//! }, -//! ) -//! .then(Ood::new().lift(...), /* make_next_stmt */ |_, _| OodStatement { .. }) -//! .then(Batching::new().lift(...), |_, ctx| BatchingStatement::from_phase_outputs(...)) -//! .then(Proximity::new(...).lift(...), |_, ctx| ProximityStatement { .. }) -//! .assemble(|reduced, parts, ctx| WARPProof { -//! rt_0: parts.pesat.td_0_root, -//! mu_i: parts.pesat.mus, -//! // ... pluck from named parts, not nested tuples -//! }); -//! ``` -//! -//! Swapping `Batching` for a different sumcheck variant becomes a one-line -//! type change at the pipeline definition. The phases stay -//! self-contained; the assembly function is the only place that names -//! the global proof shape. -//! -//! # Open design questions -//! -//! - **Is `Context` `&mut`?** Some phases mutate (e.g., the orchestrator -//! sampling query indices "between" Ood and Batching). A `&mut Context` -//! threaded through `prove` is one answer; an explicit "context node" -//! in the pipeline (no IOR call, just a context update) is another. -//! - **`ProverInputs` lifetime story.** Phases like -//! [`crate::protocol::phases::twin_constraint::TwinConstraintProverInputs`] -//! borrow from upstream reduced witnesses. The pipeline value can't -//! live longer than those borrows. Either (a) `prove` takes a -//! `WitnessRef` instead of `Witness`, or (b) the lifetime is on -//! `Pipeline` itself. -//! - **Verifier-side oracle handles.** Verifier `ProverInputs` carry -//! `IndexedOracle` / `EvalOracle` handles — exactly the refactor -//! already noted in -//! `[crate::protocol::phases::oracle_handle]`. Pipeline composition -//! slots in *over* that abstraction; we shouldn't conflate the two. - -use spongefish::{ProverState, VerifierState}; - -use crate::error::{ProverError, VerifierError}; -use crate::protocol::phases::IOR; - -/// Composition of one or more [`IOR`]s into a typed pipeline. -/// -/// Models WARP-shaped flows: each phase reads from its own statement -/// plus a shared `Context`, and emits a chunk of proof bytes plus its -/// reduced statement. The pipeline value owns proof assembly: callers -/// see one flat [`Pipeline::Proof`] type and one flat -/// [`Pipeline::FinalReduced`] result, not nested tuples. -/// -/// **Not yet wired.** The methods below are the minimum-viable shape; -/// no current WARP code calls them. See module docs for the worked -/// example of how the five phases would compose. -pub trait Pipeline { - /// Top-level statement supplied to the pipeline. Each phase derives - /// its own statement from this plus the upstream reduction. - type Statement; - /// Top-level witness. Each phase projects out the slice it needs. - type Witness; - /// Orchestrator-owned shared state read by phases. WARP's - /// `acc_instance`, fresh witnesses, sampled queries, and inter-phase - /// derived values live here. - type Context; - /// Flat global proof value. Pipeline owns assembly. - type Proof; - /// The last phase's [`IOR::ReducedStatement`]. - type FinalReduced; - - fn prove( - &self, - prover_state: &mut ProverState, - statement: &Self::Statement, - witness: &Self::Witness, - context: &mut Self::Context, - ) -> Result<(Self::FinalReduced, Self::Proof), ProverError>; - - fn verify<'a>( - &self, - verifier_state: &mut VerifierState<'a>, - statement: &Self::Statement, - context: &mut Self::Context, - proof: &Self::Proof, - ) -> Result; -} - -/// Lift a single [`IOR`] into a one-phase [`Pipeline`]. -/// -/// The user-supplied closures bridge [`IOR`]'s per-phase types -/// (`Statement`, `Witness`, `ProverInputs`, `VerifierInputs`) to the -/// pipeline-level types — they're how a phase declares "here's how I -/// project myself out of the pipeline-level state." -/// -/// **Sketch only.** Concrete implementation deferred until the lifetime -/// design (open question #2 in module docs) is settled — the closures -/// likely need to return references rather than owned values for -/// production use. -pub struct Lift { - pub inner: I, - pub make_statement: MakeStmt, - pub make_witness: MakeWit, - pub make_prover_inputs: MakeProverInputs, - pub make_verifier_inputs: MakeVerifierInputs, - pub assemble_proof: AssembleProof, -} - -/// Sequential composition of two pipelines. -/// -/// `make_next_context` runs *between* the two phases' `prove`/`verify` -/// calls. It's where orchestrator-owned glue lives — sampling queries, -/// reading `eta`/`nu_0` off the transcript, committing to a new oracle. -/// In the WARP case, the `td_new` commit and the OOD-to-Batching -/// `zetas_prefix` assembly would land here. -pub struct Then { - pub first: A, - pub second: B, - pub make_next_context: MakeNextContext, -} - -/// Convert a pipeline's nested intermediate values into a flat -/// user-defined [`Pipeline::Proof`]. -/// -/// Wraps any `Pipeline` and replaces its `Proof` type. The `Map` closure -/// receives the inner proof + final reduced statement and returns the -/// caller's flat proof shape. This is the leaf that closes the -/// "no nested tuples leak out" property promised in the trait docs. -pub struct WithFlatProof { - pub inner: P, - pub map: Map, - pub _phantom: std::marker::PhantomData, -} - -// Generic `Lift` / `Then` / `WithFlatProof` impls deliberately omitted -// for now. The concrete composition below — `PesatTwinConstraint` — -// validates that the GAT-decoupled `IOR` trait actually composes; the -// generic combinators wait on the verifier-side oracle-handle refactor -// (open question #3 above) so the abstraction lands once, against the -// final `IOR` shape. - -// ─── Concrete demo: Pesat → TwinConstraint ──────────────────────────────── -// -// First real composition built on the GAT'd `IOR`. Holds both phases by -// value so the same pipeline can serve many `prove` calls with -// different witnesses — the lifetime decoupling that motivated the GAT -// migration. Used by `tests/composition_demo.rs`. - -use ark_codes::traits::LinearCode; -use ark_ff::{Field, PrimeField}; -use ark_mt::MerkleHasher; -use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize}; -use std::marker::PhantomData; - -use effsc::hypercube::compute_hypercube_eq_evals; - -use crate::count_ops; -use crate::crypto::merkle::{warp_scheme, WarpCommitted}; -use crate::protocol::phases::batching::{ - Batching, BatchingProverInputs, BatchingReducedStatement, BatchingStatement, - BatchingVerifierInputs, -}; -use crate::protocol::phases::ood::{Ood, OodProverInputs, OodReducedStatement, OodStatement}; -use crate::protocol::phases::oracle_handle::MerkleIndexedOracle; -use crate::protocol::phases::pesat::{ - Pesat, PesatReducedStatement, PesatReducedWitness, PesatStatement, PesatWitness, -}; -use crate::protocol::phases::proximity::{ - Proximity, ProximityProofString, ProximityProverInputs, ProximityStatement, - ProximityVerifierInputs, -}; -use crate::protocol::phases::twin_constraint::{ - TwinConstraint, TwinConstraintProverInputs, TwinConstraintReducedStatement, - TwinConstraintReducedWitness, TwinConstraintStatement, TwinConstraintWitness, -}; -use crate::protocol::query::QueryIndices; -use crate::relations::r1cs::R1CSConstraints; -use crate::relations::BundledPESAT; -use crate::types::{AccumulatorInstance, WARPProof}; - -/// Full prove-side pipeline value: Pesat → TwinConstraint → Ood → -/// Batching → Proximity. -/// -/// Holds all five phases as data plus the few `WARPParams` references -/// the inter-phase glue needs (`bundled_pesat`, `hasher`, `code_len`). -/// Constructed once, `prove` is callable many times with short-lived -/// per-batch inputs — the GAT decoupling on [`crate::protocol::phases::IOR`] -/// keeps that ergonomic. -/// -/// At this point the pipeline expresses the full prover; `WARP::prove` -/// becomes a thin wrapper that builds inputs, runs the pipeline, and -/// assembles the global proof object from the pipeline's output. -pub struct WarpPipeline<'phase, F, P, C, H> -where - F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - P: BundledPESAT, - C: LinearCode, - H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, -{ - pub pesat: Pesat<'phase, F, C, H>, - pub twin_constraint: TwinConstraint<'phase, F, H>, - pub ood: Ood<'phase, F>, - pub batching: Batching<'phase, F>, - pub proximity: Proximity<'phase, F, H>, - /// `BundledPESAT` for the inter-phase `eta` evaluation between TC - /// and Ood. Kept by reference so the pipeline value doesn't own - /// the relation. - pub bundled_pesat: &'phase P, - /// Cached for building the new-oracle Merkle commit between TC - /// and Ood (`warp_scheme(hasher.clone(), code_len)`). - pub code_len: usize, -} - -impl<'phase, F, P, C, H> WarpPipeline<'phase, F, P, C, H> -where - F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - P: BundledPESAT, - C: LinearCode, - H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize + Clone + Eq, -{ - pub fn new( - code: &'phase C, - hasher: &'phase H, - r1cs: &'phase R1CSConstraints, - bundled_pesat: &'phase P, - code_len: usize, - ) -> Self { - Self { - pesat: Pesat { - code, - hasher, - _phantom: PhantomData, - }, - twin_constraint: TwinConstraint { - r1cs, - _phantom: PhantomData, - }, - ood: Ood::new(), - batching: Batching::new(), - proximity: Proximity { - hasher, - _phantom: PhantomData, - }, - bundled_pesat, - code_len, - } - } -} - -/// Static config (dimensions + per-phase tuning). Cheap to pass by -/// reference; covers all five phases. -pub struct WarpPipelineConfig { - pub l1: usize, - pub log_l: usize, - pub log_m: usize, - pub log_n: usize, - /// Witness arity: `n - k` instance variables. Used to split the - /// reduced-z vector at the TC→Ood boundary. - pub n_minus_k: usize, - /// Number of OOD samples (Ood phase). - pub s: usize, - /// Number of shift queries (Batching + Proximity phases). - pub t: usize, - /// Number of accumulated instances (`l - l1`). Used by Proximity. - pub l2: usize, -} - -/// Per-call inputs. Lifetime `'a` is independent of the pipeline's -/// `'phase` — exactly what the GAT migration unlocked. -pub struct WarpPipelineInputs<'a, F, H> -where - F: Field, - H: MerkleHasher>, -{ - pub witnesses: &'a [Vec], - pub instances: &'a [Vec], - pub acc_witness_w: &'a [Vec], - pub acc_codewords: &'a [Vec], - /// Accumulated commitments (one per accumulated entry). Read by - /// Proximity for shift-query openings. - pub acc_td: &'a [WarpCommitted], - /// Moves into `TwinConstraintStatement`. Cloned across calls if a - /// single accumulator is reused. - pub acc_instance: AccumulatorInstance, -} - -/// Full output of the prove-side pipeline. Holds per-phase reductions, -/// inter-phase published values (`eta`, `nu_0`, `td_new`, -/// batching's `mu`), and Proximity's proof string. The orchestrator -/// (`WARP::prove`) assembles the new accumulator instance and the -/// global `WARPProof` from these. -pub struct WarpPipelineReduced -where - F: Field, - H: MerkleHasher>, -{ - pub pesat: PesatReducedStatement, - pub tc: TwinConstraintReducedStatement, - pub ood: OodReducedStatement, - pub batching: BatchingReducedStatement, - pub pesat_witness: PesatReducedWitness, - pub tc_witness: TwinConstraintReducedWitness, - /// Bundled-PESAT evaluation of the reduced witness — the `η` value - /// absorbed between TC and Ood. - pub eta: F, - /// Reduced-oracle code-eval at `tc.zeta_0` — the `ν₀` absorbed - /// between TC and Ood. - pub nu_0: F, - /// New (single-codeword) Merkle commitment to the reduced - /// codeword. Becomes the next accumulator's root. - pub td_new: WarpCommitted, - /// Split halves of the reduced witness vector `z`: `(new_x, new_w)`. - /// Become the instance/witness components of the next accumulator. - pub new_x: Vec, - pub new_w: Vec, - /// Shift query indices sampled between Ood and Batching. Used by - /// the verifier to reconstruct the same query positions and as - /// part of the data flow into Proximity. - pub queries: QueryIndices, - /// Batching's reduced witness `μ = f̂(α)`. Becomes the next - /// accumulator's `μ`. - pub batching_mu: F, - /// Proximity proof string: per-commitment opening proofs and the - /// flat shift-query answer table. Goes straight into `WARPProof`. - pub proximity_proof: ProximityProofString, -} - -impl<'phase, F, P, C, H> WarpPipeline<'phase, F, P, C, H> -where - F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - P: BundledPESAT, - C: LinearCode, - H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize + Clone + Eq, -{ - /// Run Pesat → TwinConstraint → (inter-phase glue) → Ood. - /// - /// Mirrors the orchestrator's data flow exactly: - /// - `pesat_red.mus / .taus` feed TC's `Statement` - /// - `pesat_witness.codewords` feed TC's `ProverInputs` by reference - /// - between TC and Ood: compute `eta` (bundled-PESAT eval), - /// `nu_0` (oracle eval at `tc.zeta_0`), commit to the reduced - /// codeword as `td_new`, absorb `(td_new.root, eta, nu_0)` into - /// the transcript - /// - Ood reads its samples / answers from the transcript using - /// `tc_witness.f` as the oracle - pub fn prove<'a>( - &self, - prover_state: &mut ProverState, - config: &WarpPipelineConfig, - inputs: WarpPipelineInputs<'a, F, H>, - ) -> Result, ProverError> { - // ── Phase 2: PESAT ───────────────────────────────────────────── - let (pesat_red, _, pesat_witness) = self.pesat.prove( - prover_state, - &PesatStatement { - l1: config.l1, - log_m: config.log_m, - }, - &PesatWitness { - witnesses: inputs.witnesses, - }, - &(), - )?; - - // ── Phase 3a: twin-constraint sumcheck ───────────────────────── - let (tc_red, _, tc_witness) = self.twin_constraint.prove( - prover_state, - &TwinConstraintStatement { - acc_instance: inputs.acc_instance, - l1_mus: pesat_red.mus.clone(), - l1_taus: pesat_red.taus.clone(), - log_l: config.log_l, - log_m: config.log_m, - log_n: config.log_n, - }, - &TwinConstraintWitness { - acc_witness_w: inputs.acc_witness_w, - instances: inputs.instances, - witnesses: inputs.witnesses, - }, - &TwinConstraintProverInputs { - fresh_codewords: &pesat_witness.codewords, - acc_codewords: inputs.acc_codewords, - }, - )?; - - // ── Phase 3b: TC→Ood glue (eta, nu_0, td_new commit, absorb) ── - let beta_eq_evals = compute_hypercube_eq_evals(config.log_m, &tc_red.beta_tau); - let eta = self - .bundled_pesat - .evaluate_bundled(&beta_eq_evals, &tc_witness.z) - .map_err(|_| ProverError::SpongeFish)?; - let nu_0 = tc_witness.f.query_at_point(&tc_red.zeta_0); - - let (new_x_slice, new_w_slice) = tc_witness.z.split_at(config.n_minus_k); - let new_x = new_x_slice.to_vec(); - let new_w = new_w_slice.to_vec(); - - let td_new = { - let _s = tracing::info_span!("warp_pipeline.commit_new_oracle").entered(); - count_ops!(MerkleTreeBuilds); - let scheme = warp_scheme::(self.pesat.hasher.clone(), self.code_len); - scheme.commit(&[tc_witness.f.evals().to_vec()]) - }; - prover_state.prover_message(td_new.root()); - prover_state.prover_message(&eta); - prover_state.prover_message(&nu_0); - - // ── Phase 3c: OOD ───────────────────────────────────────────── - let (ood_red, _, _) = self.ood.prove( - prover_state, - &OodStatement { - s: config.s, - log_n: config.log_n, - }, - &(), - &OodProverInputs { - oracle: &tc_witness.f, - }, - )?; - - // ── Phase 3c→3d glue: sample shift query indices ────────────── - let queries = QueryIndices::::sample(prover_state, config.log_n, config.t); - - // ── Phase 3d: batching sumcheck (DAG fan-in from tc.zeta_0 + - // ood.samples_flat + queries.evaluation_points) ─────────────── - let (batching_red, _, batching_witness) = self.batching.prove( - prover_state, - &BatchingStatement::from_phase_outputs( - tc_red.zeta_0.clone(), - &ood_red.samples_flat, - &queries.evaluation_points, - config.s, - config.t, - config.log_n, - ), - &(), - &BatchingProverInputs { - oracle: &tc_witness.f, - }, - )?; - - // ── Phase 3e: proximity (shift-query openings) ──────────────── - let (_, proximity_proof, _) = self.proximity.prove( - prover_state, - &ProximityStatement { - queries: queries.clone(), - l2: config.l2, - t: config.t, - n: self.code_len, - }, - &(), - &ProximityProverInputs { - td_0: &pesat_witness.td_0, - acc_td: inputs.acc_td, - }, - )?; - - Ok(WarpPipelineReduced { - pesat: pesat_red, - tc: tc_red, - ood: ood_red, - batching: batching_red, - pesat_witness, - tc_witness, - eta, - nu_0, - td_new, - new_x, - new_w, - queries, - batching_mu: batching_witness.mu, - proximity_proof, - }) - } -} - -/// Per-call inputs for [`WarpPipeline::verify`]. Mirror of -/// [`WarpPipelineInputs`] for the verifier side. -/// -/// The orchestrator parses the transcript prefix (`parse_statement`) -/// and slices off `acc_mu_first` from the prior accumulator before -/// calling the pipeline. Everything the pipeline phases themselves -/// read from the transcript is read inside the phases — `parsed_acc` -/// supplies the public bits the TC phase needs in its -/// `Statement`, `acc_mu_first` is the Batching final-claim multiplier, -/// and the proof feeds the Proximity-side oracle handles. -pub struct WarpPipelineVerifyInputs<'a, F, H> -where - F: Field, - H: MerkleHasher>, -{ - /// Pre-parsed accumulator instance (the `l2` accumulated entries). - /// Moves into TC's `Statement`. The orchestrator owns the - /// transcript-parsing step (`parse_statement`). - pub parsed_acc: AccumulatorInstance, - /// `acc_instance.mu[0]` from the prior single-entry accumulator. - /// Feeds Batching's `acc_mu` final-claim multiplier. - pub acc_mu_first: F, - /// The proof object the verifier received. Borrowed so the - /// orchestrator keeps its own copy for any post-pipeline checks. - pub proof: &'a WARPProof, -} - -/// Output of the verify-side pipeline. -/// -/// Holds the values the orchestrator (`WARP::verify`) still needs after -/// the pipeline returns: the post-Batching consistency checks against -/// the prior accumulator's `α` and `β` are orchestrator-owned because -/// they cross-reference data outside the pipeline's scope (the -/// `acc_alpha_first` / `acc_beta_*_first` cached before the call). -/// -/// Kept minimal — only the fields `WARP::verify` reads after the -/// inline-pipeline block ends. -pub struct WarpPipelineVerified -where - F: Field, - H: MerkleHasher>, -{ - /// PESAT's reduced statement — the orchestrator reads `taus` - /// (the `l1_taus`) for the post-pipeline `β` consistency check. - pub pesat: PesatReducedStatement, - /// TwinConstraint's reduced statement — the orchestrator reads - /// `gamma` (for `gamma_eq_evals`) when assembling the expected - /// `β` from the accumulated and fresh `(τ, x)` pairs. - pub tc: TwinConstraintReducedStatement, - /// OOD's reduced statement (kept for completeness / debugging; - /// post-pipeline checks don't read it directly). - pub ood: OodReducedStatement, - /// Batching's reduced statement — the orchestrator checks - /// `acc_alpha_first == batching.alpha`. - pub batching: BatchingReducedStatement, - /// `(η, ν₀)` read off the transcript between TC and OOD. Kept so - /// callers debugging a verify failure can inspect them; not - /// required for the post-pipeline checks. - pub eta: F, - pub nu_0: F, - /// Shift query indices sampled between OOD and Batching. Kept for - /// the same reason as `eta` / `nu_0`. - pub queries: QueryIndices, - /// Recomputed `ν` vector (`1 + s + t`) — Batching's input. Kept - /// for debugging. - pub nus: Vec, - /// Phantom binding for the hasher type; the digest type appears - /// in `parsed_acc` but the verified output itself doesn't carry - /// it. - pub _phantom: PhantomData, -} - -impl<'phase, F, P, C, H> WarpPipeline<'phase, F, P, C, H> -where - F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - P: BundledPESAT, - C: LinearCode, - H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize + Clone + Eq, -{ - /// Run the verifier-side pipeline: - /// PESAT::verify → TC::verify → (read `td_digest, η, ν₀`, - /// discharge TC's deferred oracle check) → OOD::verify → (sample - /// shift queries, arity checks, build Merkle oracle handles) → - /// Proximity::verify → (recompute `ν` vector) → Batching::verify. - /// - /// Mirrors [`WarpPipeline::prove`] block-for-block. The - /// orchestrator handles `parse_statement` and the post-pipeline - /// `(α, β)` consistency checks against the prior accumulator — - /// those cross-reference data outside the pipeline's scope. - pub fn verify<'verifier, 'a>( - &self, - verifier_state: &mut VerifierState<'verifier>, - config: &WarpPipelineConfig, - inputs: WarpPipelineVerifyInputs<'a, F, H>, - ) -> Result, VerifierError> { - let n = self.code_len; - - // Cache the accumulated roots before `parsed_acc` moves into TC's - // statement — Proximity needs them for the per-acc Merkle handles. - let l2_roots = inputs.parsed_acc.rt.clone(); - - // ── Phase 2: PESAT ───────────────────────────────────────────── - let (pesat_red, pesat_v_out) = self.pesat.verify( - verifier_state, - &PesatStatement { - l1: config.l1, - log_m: config.log_m, - }, - &(), - )?; - let rt_0 = pesat_v_out.rt_0; - - // ── Phase 3a: twin-constraint sumcheck ───────────────────────── - let (tc_red, _) = self.twin_constraint.verify( - verifier_state, - &TwinConstraintStatement { - acc_instance: inputs.parsed_acc, - l1_mus: pesat_red.mus.clone(), - l1_taus: pesat_red.taus.clone(), - log_l: config.log_l, - log_m: config.log_m, - log_n: config.log_n, - }, - &(), - )?; - - // ── Phase 3b: TC→Ood glue (read td_digest, η, ν₀, discharge) ── - let _td_digest: H::Digest = verifier_state.prover_message()?; - let eta: F = verifier_state.prover_message()?; - let nu_0: F = verifier_state.prover_message()?; - tc_red.deferred.discharge(&tc_red.gamma, nu_0, eta)?; - - // ── Phase 3c: OOD ───────────────────────────────────────────── - let (ood_red, _) = self.ood.verify( - verifier_state, - &OodStatement { - s: config.s, - log_n: config.log_n, - }, - &(), - )?; - - // ── Phase 3c→3d glue: sample shift query indices, arity checks, - // build Merkle oracle handles ──────────────────────────────── - let n_shift_query_bytes = (config.t * config.log_n).div_ceil(8); - let bytes_shift_queries: Vec = (0..n_shift_query_bytes) - .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) - .collect(); - let queries: QueryIndices = - QueryIndices::from_squeezed_bytes(&bytes_shift_queries, config.log_n, config.t); - - // Whole-proof arity checks. The pipeline owns these — every - // phase past this point reads from `proof` and assumes the - // shape matches. - (inputs.proof.shift_query_answers.len() == config.t) - .then_some(()) - .ok_or(VerifierError::NumShiftQueries)?; - (inputs.proof.auth_j.len() == config.l2) - .then_some(()) - .ok_or(VerifierError::NumL2Instances)?; - - // Build (sorted, unique) leaf positions and a row-mapping into - // the shift_query_answers table — same construction as the - // prover side / inline `WARP::verify`. - let mut indexed: Vec<(usize, usize)> = queries - .leaf_positions - .iter() - .copied() - .enumerate() - .map(|(row, pos)| (pos, row)) - .collect(); - indexed.sort_by_key(|&(pos, _)| pos); - indexed.dedup_by_key(|&mut (pos, _)| pos); - let sorted_unique: Vec = indexed.iter().map(|&(p, _)| p).collect(); - let row_indices: Vec = indexed.iter().map(|&(_, r)| r).collect(); - - let fresh_values: Vec> = row_indices - .iter() - .map(|&r| inputs.proof.shift_query_answers[r][config.l2..].to_vec()) - .collect(); - let fresh_handle = MerkleIndexedOracle::new( - warp_scheme::(self.pesat.hasher.clone(), n), - &rt_0, - &inputs.proof.auth_0, - sorted_unique.clone(), - fresh_values, - ); - - let acc_handles: Vec> = (0..config.l2) - .map(|k| { - let acc_values: Vec> = row_indices - .iter() - .map(|&r| vec![inputs.proof.shift_query_answers[r][k]]) - .collect(); - MerkleIndexedOracle::new( - warp_scheme::(self.pesat.hasher.clone(), n), - &l2_roots[k], - &inputs.proof.auth_j[k], - sorted_unique.clone(), - acc_values, - ) - }) - .collect(); - - // ── Phase 3e: proximity (shift-query openings) ──────────────── - self.proximity.verify( - verifier_state, - &ProximityStatement { - queries: queries.clone(), - l2: config.l2, - t: config.t, - n, - }, - &ProximityVerifierInputs { - fresh: &fresh_handle, - acc: &acc_handles, - _f: PhantomData, - }, - )?; - - // ── Recompute ν vector from shift answers ──────────────────── - let gamma_eq_evals = compute_hypercube_eq_evals(config.log_l, &tc_red.gamma); - let mut nus = Vec::with_capacity(1 + config.s + config.t); - nus.push(nu_0); - nus.extend(ood_red.answers.iter().copied()); - for v_jk in inputs.proof.shift_query_answers.iter() { - let nu_st = v_jk - .iter() - .zip(&gamma_eq_evals) - .fold(F::zero(), |acc, (v, eq)| acc + *eq * *v); - nus.push(nu_st); - } - - // ── Phase 3d: batching sumcheck ────────────────────────────── - let (batching_red, _) = self.batching.verify( - verifier_state, - &BatchingStatement::from_phase_outputs( - tc_red.zeta_0.clone(), - &ood_red.samples_flat, - &queries.evaluation_points, - config.s, - config.t, - config.log_n, - ), - &BatchingVerifierInputs { - nus: nus.clone(), - acc_mu: inputs.acc_mu_first, - }, - )?; - - Ok(WarpPipelineVerified { - pesat: pesat_red, - tc: tc_red, - ood: ood_red, - batching: batching_red, - eta, - nu_0, - queries, - nus, - _phantom: PhantomData, - }) - } -} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index e44d6c4..55ed4d7 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,6 +1,4 @@ -pub mod composition; pub mod oracle; pub mod phases; pub mod query; -pub mod then; pub mod transcript; diff --git a/src/protocol/phases/bridge.rs b/src/protocol/phases/bridge.rs new file mode 100644 index 0000000..07c5d05 --- /dev/null +++ b/src/protocol/phases/bridge.rs @@ -0,0 +1,271 @@ +//! TC → OOD bridge phase. +//! +//! After TwinConstraint reduces to `(γ, ζ₀, β_τ)` on the verifier side +//! and `(γ, ζ₀, β_τ, deferred, f, z)` on the prover side, three new +//! values get published into the transcript: +//! +//! - `η = ⟨ eq(β_τ), p(z) ⟩` — bundled-PESAT evaluation +//! - `ν₀ = f̂(ζ₀)` — reduced-oracle evaluation at the new code point +//! - `td_new` — Merkle commit to `f`'s codeword (becomes the next +//! accumulator's root) +//! +//! The prover also splits `z` into `(new_x, new_w) = z[..N-k], z[N-k..]` +//! for the next accumulator's `(β.1, witness.w)` slots. +//! +//! The verifier reads `(td_digest, η, ν₀)` from the transcript and +//! discharges TwinConstraint's deferred oracle check: +//! +//! ```text +//! eq(τ, γ) · (ν₀ + ω · η) ≟ final_claim +//! ``` +//! +//! Modeled as an IOR for uniformity with the rest of the choreography, +//! even though it has no verifier challenges — Bridge is "0-round, +//! prover sends three values, verifier checks deferred." Same shape as +//! the IOR trait, just with degenerate fields where they don't apply. +//! +//! IOR signature +//! ------------- +//! - `Statement` — `(ζ₀, β_τ, log_m, n_minus_k)` +//! - `Witness` — `(z, f)` — prover's TC-side reduced state, borrowed +//! - `ProverInputs` — `(bundled_pesat, hasher, code_len)` — config refs +//! - `VerifierInputs` — `(deferred, γ)` — what's needed to discharge TC +//! - `ReductionInputs` — `(η, ν₀, td_new_root)` — transcript-published values +//! - `ReducedStatement` — same as `ReductionInputs` (the publish is the reduction) +//! - `ProofString` — `()` (everything goes through transcript writes / reads) +//! - `ReducedWitness` — `(td_new, new_x, new_w)` — fed into the new accumulator +//! - `VerifierOutputs` — `()` + +use ark_ff::Field; +use ark_mt::MerkleHasher; +use effsc::hypercube::compute_hypercube_eq_evals; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; +use std::marker::PhantomData; + +use crate::count_ops; +use crate::crypto::merkle::{warp_scheme, WarpCommitted}; +use crate::error::{ProverError, VerifierError}; +use crate::protocol::oracle::Oracle; +use crate::protocol::phases::twin_constraint::DeferredOracleCheck; +use crate::protocol::phases::IOR; +use crate::relations::BundledPESAT; + +pub struct BridgeStatement { + pub zeta_0: Vec, + pub beta_tau: Vec, + pub log_m: usize, + pub n_minus_k: usize, +} + +pub struct BridgeWitness<'a, F: Field> { + pub z: &'a [F], + pub f: &'a Oracle, +} + +pub struct BridgeProverInputs<'a, F, P, H> +where + F: Field, + P: BundledPESAT, + H: MerkleHasher>, +{ + pub bundled_pesat: &'a P, + pub hasher: &'a H, + pub code_len: usize, + pub _f: PhantomData, +} + +pub struct BridgeVerifierInputs<'a, F: Field> { + pub deferred: &'a DeferredOracleCheck, + pub gamma: &'a [F], +} + +pub struct BridgeReductionInputs { + pub eta: F, + pub nu_0: F, + pub td_new_root: H::Digest, +} + +pub struct BridgeReducedStatement { + pub eta: F, + pub nu_0: F, + pub td_new_root: H::Digest, +} + +pub struct BridgeReducedWitness +where + F: Field, + H: MerkleHasher>, +{ + pub td_new: WarpCommitted, + pub new_x: Vec, + pub new_w: Vec, +} + +/// TC → OOD bridge phase configuration. Stateless; the lifetime +/// parameter exists only to anchor `ProverInputs<'a>` / `Witness<'a>` +/// for the IOR trait impl. +pub struct Bridge<'a, F, P, H> +where + F: Field, + P: BundledPESAT, + H: MerkleHasher>, +{ + pub _phantom: PhantomData<(&'a F, &'a P, &'a H)>, +} + +impl<'a, F, P, H> Bridge<'a, F, P, H> +where + F: Field, + P: BundledPESAT, + H: MerkleHasher>, +{ + pub fn new() -> Self { + Self { + _phantom: PhantomData, + } + } +} + +impl<'a, F, P, H> Default for Bridge<'a, F, P, H> +where + F: Field, + P: BundledPESAT, + H: MerkleHasher>, +{ + fn default() -> Self { + Self::new() + } +} + +impl<'a, F, P, H> IOR for Bridge<'a, F, P, H> +where + F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + P: BundledPESAT, + H: MerkleHasher>, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize + Clone, +{ + type Statement = BridgeStatement; + type Witness<'b> + = BridgeWitness<'b, F> + where + Self: 'b; + type ProverInputs<'b> + = BridgeProverInputs<'b, F, P, H> + where + Self: 'b; + type VerifierInputs<'b> + = BridgeVerifierInputs<'b, F> + where + Self: 'b; + type ReductionInputs = BridgeReductionInputs; + type ReducedStatement = BridgeReducedStatement; + type ProofString = (); + type ReducedWitness = BridgeReducedWitness; + type VerifierOutputs = (); + + fn reduce_statement( + &self, + _statement: &Self::Statement, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement { + BridgeReducedStatement { + eta: inputs.eta, + nu_0: inputs.nu_0, + td_new_root: inputs.td_new_root.clone(), + } + } + + #[tracing::instrument( + name = "bridge", + skip_all, + fields(log_m = statement.log_m, n_minus_k = statement.n_minus_k) + )] + fn prove_inner<'b>( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + witness: &Self::Witness<'b>, + inputs: &Self::ProverInputs<'b>, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > + where + Self: 'b, + { + // η = ⟨ eq(β_τ), p(z) ⟩ + let beta_eq_evals = compute_hypercube_eq_evals(statement.log_m, &statement.beta_tau); + let eta = inputs + .bundled_pesat + .evaluate_bundled(&beta_eq_evals, witness.z) + .map_err(|_| ProverError::SpongeFish)?; + + // ν₀ = f̂(ζ₀) + let nu_0 = witness.f.query_at_point(&statement.zeta_0); + + // (new_x, new_w) = z[..N-k], z[N-k..] + let (new_x_slice, new_w_slice) = witness.z.split_at(statement.n_minus_k); + let new_x = new_x_slice.to_vec(); + let new_w = new_w_slice.to_vec(); + + // td_new ← Merkle.commit(f.evals()) + let td_new = { + let _s = tracing::info_span!("bridge.commit_new_oracle").entered(); + count_ops!(MerkleTreeBuilds); + let scheme = warp_scheme::(inputs.hasher.clone(), inputs.code_len); + scheme.commit(&[witness.f.evals().to_vec()]) + }; + let td_new_root = td_new.root().clone(); + + // Absorb (td_new.root, η, ν₀) + prover_state.prover_message(&td_new_root); + prover_state.prover_message(&eta); + prover_state.prover_message(&nu_0); + + Ok(( + BridgeReductionInputs { + eta, + nu_0, + td_new_root, + }, + (), + BridgeReducedWitness { + td_new, + new_x, + new_w, + }, + )) + } + + #[tracing::instrument(name = "bridge.verify", skip_all)] + fn verify_inner<'b, 'c>( + &self, + verifier_state: &mut VerifierState<'b>, + _statement: &Self::Statement, + inputs: &Self::VerifierInputs<'c>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + where + Self: 'c, + { + // Read (td_digest, η, ν₀) from the transcript. + let td_new_root: H::Digest = verifier_state.prover_message()?; + let eta: F = verifier_state.prover_message()?; + let nu_0: F = verifier_state.prover_message()?; + + // Discharge TC's deferred check. + inputs.deferred.discharge(inputs.gamma, nu_0, eta)?; + + Ok(( + BridgeReductionInputs { + eta, + nu_0, + td_new_root, + }, + (), + )) + } +} diff --git a/src/protocol/phases/mod.rs b/src/protocol/phases/mod.rs index 0e79758..2680e4e 100644 --- a/src/protocol/phases/mod.rs +++ b/src/protocol/phases/mod.rs @@ -27,10 +27,12 @@ #![allow(clippy::type_complexity)] pub mod batching; +pub mod bridge; pub mod ood; pub mod oracle_handle; pub mod pesat; pub mod proximity; +pub mod sample_queries; pub mod twin_constraint; use spongefish::{ProverState, VerifierState}; diff --git a/src/protocol/phases/sample_queries.rs b/src/protocol/phases/sample_queries.rs new file mode 100644 index 0000000..29d04d5 --- /dev/null +++ b/src/protocol/phases/sample_queries.rs @@ -0,0 +1,135 @@ +//! Shift-query sampling phase. +//! +//! Between OOD and Batching, the verifier draws `t · log_n` random +//! bits and decodes them into `t` shift-query positions over +//! `{0, 1}^log_n`, plus their corresponding evaluation-point vectors. +//! +//! This is the simplest possible IOR — one round of pure verifier +//! randomness, no prover messages, no oracles. Modeled as an IOR for +//! uniformity with the rest of the choreography. +//! +//! IOR signature +//! ------------- +//! - `Statement` — `(log_n, t)` +//! - `Witness` — `()` +//! - `ProverInputs` — `()` +//! - `VerifierInputs` — `()` +//! - `ReductionInputs` — the sampled [`QueryIndices`] +//! - `ReducedStatement` — same — both sides receive the queries +//! - `ProofString` — `()` +//! - `ReducedWitness` — `()` (no asymmetric data: both sides hold the same queries) +//! - `VerifierOutputs` — `()` + +use ark_ff::Field; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; +use std::marker::PhantomData; + +use crate::error::{ProverError, VerifierError}; +use crate::protocol::phases::IOR; +use crate::protocol::query::QueryIndices; + +pub struct SampleQueriesStatement { + pub log_n: usize, + pub t: usize, +} + +pub struct SampleQueriesReductionInputs { + pub queries: QueryIndices, +} + +pub struct SampleQueriesReducedStatement { + pub queries: QueryIndices, +} + +/// Shift-query sampling phase configuration. Stateless. +pub struct SampleQueries<'a, F: Field> { + pub _phantom: PhantomData<&'a F>, +} + +impl<'a, F: Field> SampleQueries<'a, F> { + pub fn new() -> Self { + Self { + _phantom: PhantomData, + } + } +} + +impl<'a, F: Field> Default for SampleQueries<'a, F> { + fn default() -> Self { + Self::new() + } +} + +impl<'a, F> IOR for SampleQueries<'a, F> +where + F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, +{ + type Statement = SampleQueriesStatement; + type Witness<'b> + = () + where + Self: 'b; + type ProverInputs<'b> + = () + where + Self: 'b; + type VerifierInputs<'b> + = () + where + Self: 'b; + type ReductionInputs = SampleQueriesReductionInputs; + type ReducedStatement = SampleQueriesReducedStatement; + type ProofString = (); + type ReducedWitness = (); + type VerifierOutputs = (); + + fn reduce_statement( + &self, + _statement: &Self::Statement, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement { + SampleQueriesReducedStatement { + queries: inputs.queries.clone(), + } + } + + #[tracing::instrument(name = "sample_queries", skip_all, fields(t = statement.t, log_n = statement.log_n))] + fn prove_inner<'b>( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + _witness: &Self::Witness<'b>, + _inputs: &Self::ProverInputs<'b>, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > + where + Self: 'b, + { + let queries = QueryIndices::::sample(prover_state, statement.log_n, statement.t); + Ok((SampleQueriesReductionInputs { queries }, (), ())) + } + + #[tracing::instrument(name = "sample_queries.verify", skip_all)] + fn verify_inner<'b, 'c>( + &self, + verifier_state: &mut VerifierState<'b>, + statement: &Self::Statement, + _inputs: &Self::VerifierInputs<'c>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + where + Self: 'c, + { + let n_bytes = (statement.t * statement.log_n).div_ceil(8); + let bytes: Vec = (0..n_bytes) + .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) + .collect(); + let queries = QueryIndices::from_squeezed_bytes(&bytes, statement.log_n, statement.t); + Ok((SampleQueriesReductionInputs { queries }, ())) + } +} diff --git a/src/protocol/then.rs b/src/protocol/then.rs deleted file mode 100644 index d1f185f..0000000 --- a/src/protocol/then.rs +++ /dev/null @@ -1,257 +0,0 @@ -//! Generic [`Then`] combinator over [`Pipeline`]. -//! -//! Demonstrates that [`crate::protocol::composition::Pipeline`] actually -//! composes — without forcing every consumer to hand-write a -//! [`crate::protocol::composition::WarpPipeline`]-style struct each time. -//! -//! # Design choices (first cut) -//! -//! 1. **Witness threading.** `Then::Witness = (A::Witness, B::Witness)`. -//! Caller supplies a tuple, `prove` destructures. Simple, but does not -//! scale gracefully past 2 phases — chains nest as -//! `((A::W, B::W), C::W)` etc. Follow-up: introduce a -//! `WitnessProjection` trait so each phase plucks its slice from a -//! flat top-level witness, eliminating the nesting. -//! 2. **Context.** Shared. `A::Context = B::Context = Then::Context`. -//! Closer to the [`crate::protocol::composition::WarpPipeline`] data -//! flow (one orchestrator-owned struct read by all phases). Alternative -//! would be `Then::Context = (A::Context, B::Context)` — split per -//! sub-pipeline — but inter-phase glue (e.g. WARP's `eta` / `nu_0` / -//! `td_new`) is exactly the kind of thing that wants to land in shared -//! state, not in B's private context. -//! 3. **Transition function shape.** Closure rather than dedicated trait. -//! More ergonomic at call sites; the trait variant -//! (`trait Transition { fn transition(...); }`) is more -//! inspectable / nameable at the type level — switch if the closure -//! blanket impls cause inference problems for downstream users. -//! -//! # Proof shape -//! -//! `Then::Proof = (A::Proof, B::Proof)` — nests for now. A -//! `WithFlatProof` adapter (sketched in -//! [`crate::protocol::composition::WithFlatProof`]) is the planned -//! follow-up that maps the nested tuple to a flat caller-supplied proof -//! type. -//! -//! # What this file is NOT -//! -//! Not a replacement for `WarpPipeline`. `WarpPipeline` is the concrete, -//! hand-tuned five-phase composition with known WARP-specific glue -//! (committing `td_new`, sampling shift queries between Ood and -//! Batching, etc). `Then` is the generic combinator that proves the -//! abstraction composes; once verifier-side oracle handles land -//! (`crate::protocol::phases::oracle_handle`) and `Pipeline::verify` -//! is wired on `WarpPipeline`, the WARP composition can be expressed -//! as a chain of `Then` combinators if desired. - -use spongefish::{ProverState, VerifierState}; - -use crate::error::{ProverError, VerifierError}; -use crate::protocol::composition::Pipeline; - -/// Sequential composition of two [`Pipeline`]s. -/// -/// Holds `first` and `second` by value plus a `transition` value (a -/// `(prove_fn, verify_fn)` closure pair) that runs *between* the two -/// pipelines' `prove` (or `verify`) calls. The transition is where -/// orchestrator-owned glue lives — reading challenges off the -/// transcript, deriving `B`'s statement from `A`'s reduced output, -/// updating shared context. -/// -/// See module docs for the design choices behind the associated-type -/// shapes (witness tuple, shared context, closure-based transition). -pub struct Then { - pub first: A, - pub second: B, - pub transition: Trans, -} - -impl Then { - /// Build a `Then` combinator. `transition` is the inter-phase glue - /// — see the [`Pipeline`] impl below for its required shape. - pub fn new(first: A, second: B, transition: Trans) -> Self { - Self { - first, - second, - transition, - } - } -} - -/// Closure-pair-based [`Pipeline`] composition. -/// -/// `Trans` is `(FProve, FVerify)`. Most call sites only need the -/// matching half — at the time the pipeline value is constructed only -/// one side runs — but the type-level honesty of "a transition is a -/// pair of closures" matches the trait's prove/verify symmetry. -/// Callers using only one side can pass any no-op closure for the -/// other slot. -/// -/// Both closures receive: -/// - `&mut ProverState` / `&mut VerifierState` so they can sample -/// challenges or absorb glue values into the transcript, -/// - `&A::FinalReduced` — A's reduced output, -/// - `&mut Context` — the shared orchestrator-owned state, -/// -/// and return `B`'s statement. -impl Pipeline for Then -where - A: Pipeline, - B: Pipeline, - FProve: Fn(&mut ProverState, &A::FinalReduced, &mut A::Context) -> B::Statement, - FVerify: for<'a> Fn(&mut VerifierState<'a>, &A::FinalReduced, &mut A::Context) -> B::Statement, -{ - type Statement = A::Statement; - type Witness = (A::Witness, B::Witness); - type Context = A::Context; - type Proof = (A::Proof, B::Proof); - type FinalReduced = B::FinalReduced; - - fn prove( - &self, - prover_state: &mut ProverState, - statement: &Self::Statement, - witness: &Self::Witness, - context: &mut Self::Context, - ) -> Result<(Self::FinalReduced, Self::Proof), ProverError> { - let (a_witness, b_witness) = witness; - let (a_reduced, a_proof) = - self.first.prove(prover_state, statement, a_witness, context)?; - let b_statement = (self.transition.0)(prover_state, &a_reduced, context); - let (b_reduced, b_proof) = - self.second - .prove(prover_state, &b_statement, b_witness, context)?; - Ok((b_reduced, (a_proof, b_proof))) - } - - fn verify<'a>( - &self, - verifier_state: &mut VerifierState<'a>, - statement: &Self::Statement, - context: &mut Self::Context, - proof: &Self::Proof, - ) -> Result { - let (a_proof, b_proof) = proof; - let a_reduced = self - .first - .verify(verifier_state, statement, context, a_proof)?; - let b_statement = (self.transition.1)(verifier_state, &a_reduced, context); - let b_reduced = self - .second - .verify(verifier_state, &b_statement, context, b_proof)?; - Ok(b_reduced) - } -} - -#[cfg(test)] -mod tests { - //! Type-level validation: the generic `Then` combinator typechecks - //! and, at runtime, runs both child pipelines in order. Crypto is - //! deliberately absent — the fake `Pipeline` implementations below - //! exist solely to drive `Then::prove` through the trait machinery. - //! - //! What we actually assert: - //! 1. `Then::prove` calls `A::prove`, then the transition closure, - //! then `B::prove`, in that order. - //! 2. The shared context is threaded through all three steps. - //! 3. `B`'s statement is derived from `A`'s reduced output (the - //! transition multiplies by 10). - //! 4. `Then::Proof = (A::Proof, B::Proof)` lands the nested tuple. - - use super::*; - - /// Test-only ordered event log. Each fake pipeline's `prove` pushes - /// an entry; the transition closure pushes its own. The final - /// vector is the assertion target. - #[derive(Default)] - struct Trace(Vec); - - /// Trivial fake pipeline — does no cryptography. `prove` records - /// its label + the witness value into the shared trace and returns - /// `stmt + wit` as the "reduction" so the transition has something - /// nontrivial to read. - struct FakePipeline { - label: &'static str, - } - - impl Pipeline for FakePipeline { - type Statement = u32; - type Witness = u32; - type Context = Trace; - type Proof = u32; - type FinalReduced = u32; - - fn prove( - &self, - _prover_state: &mut ProverState, - statement: &Self::Statement, - witness: &Self::Witness, - context: &mut Self::Context, - ) -> Result<(Self::FinalReduced, Self::Proof), ProverError> { - context.0.push(format!( - "{}.prove(stmt={}, wit={})", - self.label, statement, witness - )); - Ok((statement + witness, *witness)) - } - - fn verify<'a>( - &self, - _verifier_state: &mut VerifierState<'a>, - _statement: &Self::Statement, - _context: &mut Self::Context, - _proof: &Self::Proof, - ) -> Result { - unimplemented!("verify side not exercised by this test"); - } - } - - #[test] - fn then_runs_first_then_transition_then_second() { - let pipeline = Then::new( - FakePipeline { label: "A" }, - FakePipeline { label: "B" }, - ( - // Prove-side transition: log + derive B's stmt from A's - // reduced output. Multiplies by 10 — arbitrary - // deterministic transform we can assert on. - |_ps: &mut ProverState, a_reduced: &u32, ctx: &mut Trace| -> u32 { - ctx.0.push(format!("transition(a_reduced={})", a_reduced)); - a_reduced * 10 - }, - // Verify-side closure — never invoked in this test. - |_vs: &mut VerifierState, _r: &u32, _c: &mut Trace| -> u32 { 0 }, - ), - ); - - let mut prover_state = spongefish::domain_separator!("test::then::compose") - .without_session() - .instance(&0u32) - .std_prover(); - - let mut trace = Trace::default(); - let witness: (u32, u32) = (3, 7); - let statement: u32 = 5; - - let (final_reduced, proof) = pipeline - .prove(&mut prover_state, &statement, &witness, &mut trace) - .expect("composition should run"); - - // Order: A.prove → transition → B.prove. - assert_eq!( - trace.0.len(), - 3, - "expected 3 events; got {:?}", - trace.0 - ); - assert_eq!(trace.0[0], "A.prove(stmt=5, wit=3)"); - // A's reduced = stmt(5) + wit(3) = 8 → B's stmt = 80. - assert_eq!(trace.0[1], "transition(a_reduced=8)"); - assert_eq!(trace.0[2], "B.prove(stmt=80, wit=7)"); - - // Final reduced = B's reduced = B.stmt + B.wit = 80 + 7 = 87. - assert_eq!(final_reduced, 87); - // Proof = (A.proof, B.proof) = (A.wit, B.wit) = (3, 7). - assert_eq!(proof, (3, 7)); - } -} diff --git a/tests/composition_demo.rs b/tests/composition_demo.rs deleted file mode 100644 index 497dddf..0000000 --- a/tests/composition_demo.rs +++ /dev/null @@ -1,187 +0,0 @@ -//! Demo test for `PesatTwinConstraint` — the first concrete composition -//! built on the GAT-decoupled `IOR` trait. -//! -//! What this validates: -//! -//! 1. The pipeline value holds both phases (`Pesat` + `TwinConstraint`) -//! by data, with their `'phase` lifetime tied to long-lived params. -//! 2. `pipeline.prove(...)` is callable many times in the same scope -//! with **different short-lived witnesses each time**. Pre-GAT -//! migration this would not compile — the witness's lifetime was -//! forced to match the phase struct's lifetime, so the first call's -//! borrows would have been pinned to the pipeline's lifetime, and -//! a second call with different inputs wouldn't have type-checked. -//! 3. The Pesat → TwinConstraint data flow is wired correctly: -//! `pesat_red.mus` / `pesat_red.taus` feed into TC's statement, and -//! `pesat_red_wit.codewords` feeds into TC's prover inputs by -//! reference (no clone). - -use ark_bls12_381::Fr as BLS12_381; -use ark_codes::{ - reed_solomon::{config::ReedSolomonConfig, ReedSolomon}, - traits::LinearCode, -}; -use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; -use ark_ff::UniformRand; -use ark_mt::blake3::Blake3FieldHasher; -use ark_std::rand::thread_rng; - -use warp::protocol::composition::{WarpPipeline, WarpPipelineConfig, WarpPipelineInputs}; -use warp::relations::{ - r1cs::hashchain::{compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness}, - BundledPESAT, Relation, ToPolySystem, -}; -use warp::types::AccumulatorInstance; -use warp::utils::poseidon; - -type F = BLS12_381; -type H = Blake3FieldHasher; - -/// Build one `(instance, witness)` batch of size `l1`. -fn make_batch( - l1: usize, - poseidon_config: &ark_crypto_primitives::sponge::poseidon::PoseidonConfig, - hash_chain_size: usize, - rng: &mut impl ark_std::rand::Rng, -) -> (Vec>, Vec>) { - (0..l1) - .map(|_| { - let preimage = vec![F::rand(rng)]; - let instance = HashChainInstance { - digest: compute_hash_chain::>(poseidon_config, &preimage, hash_chain_size), - }; - let witness = HashChainWitness::>::new(preimage); - let relation = HashChainRelation::, CRHGadget<_>>::new( - instance, - witness, - (poseidon_config.clone(), hash_chain_size), - ); - (relation.x, relation.w) - }) - .unzip() -} - -#[test] -fn pipeline_runs_pesat_tc_ood_twice() { - // ── setup: long-lived ───────────────────────────────────────────── - let l1 = 4; - let s = 8; - let t = 7; - let log_l = (l1 as f64).log2() as usize; - let hash_chain_size = 4; - let mut rng = thread_rng(); - let poseidon_config = poseidon::initialize_poseidon_config::(); - let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( - poseidon_config.clone(), - hash_chain_size, - )) - .unwrap(); - let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); - let code = ReedSolomon::new(code_config); - let hasher = Blake3FieldHasher::::new(); - let r1cs_constraints = r1cs.constraints().clone(); - - let n = code.code_len(); - let log_n = (n as f64).log2() as usize; - - let config = WarpPipelineConfig { - l1, - log_l, - log_m: r1cs.log_m, - log_n, - n_minus_k: r1cs.n - r1cs.k, - s, - t, - l2: 0, - }; - - // Build the pipeline ONCE — borrows code / hasher / r1cs / bundled - // PESAT relation for 'phase. - let pipeline = - WarpPipeline::::new(&code, &hasher, &r1cs_constraints, &r1cs, n); - - // ── two independent batches with independent lifetimes ──────────── - let (instances_a, witnesses_a) = make_batch(l1, &poseidon_config, hash_chain_size, &mut rng); - let (instances_b, witnesses_b) = make_batch(l1, &poseidon_config, hash_chain_size, &mut rng); - - let acc_w_empty: Vec> = vec![]; - let acc_codewords_empty: Vec> = vec![]; - - // Call A: borrows witnesses_a / instances_a. - let mut prover_state_a = spongefish::domain_separator!("test::composition_demo") - .without_session() - .instance(&0u32) - .std_prover(); - let red_a = pipeline - .prove( - &mut prover_state_a, - &config, - WarpPipelineInputs { - witnesses: &witnesses_a, - instances: &instances_a, - acc_witness_w: &acc_w_empty, - acc_codewords: &acc_codewords_empty, - acc_td: &[], - acc_instance: AccumulatorInstance::::empty(), - }, - ) - .expect("first prove call should succeed"); - - // Call B: borrows witnesses_b / instances_b. The pipeline value is - // still alive — this is what would not compile without GATs. - let mut prover_state_b = spongefish::domain_separator!("test::composition_demo") - .without_session() - .instance(&0u32) - .std_prover(); - let red_b = pipeline - .prove( - &mut prover_state_b, - &config, - WarpPipelineInputs { - witnesses: &witnesses_b, - instances: &instances_b, - acc_witness_w: &acc_w_empty, - acc_codewords: &acc_codewords_empty, - acc_td: &[], - acc_instance: AccumulatorInstance::::empty(), - }, - ) - .expect("second prove call should succeed"); - - // Shape sanity across all three phases. - assert_eq!(red_a.pesat.mus.len(), l1); - assert_eq!(red_a.pesat.taus.len(), l1); - assert_eq!(red_a.tc.gamma.len(), log_l); - assert_eq!(red_a.tc.zeta_0.len(), log_n); - // Ood: s answers, samples_flat is s * log_n long. - assert_eq!(red_a.ood.answers.len(), s); - assert_eq!(red_a.ood.samples_flat.len(), s * log_n); - - // Inter-phase glue produced sensible split halves. - assert_eq!(red_a.new_x.len(), r1cs.n - r1cs.k); - assert_eq!(red_a.new_w.len(), r1cs.k); - - // Batching: t shift queries sampled, alpha is the new code-eval point - // (a log_n-length challenge vector). - assert_eq!(red_a.queries.evaluation_points.len(), t); - assert_eq!(red_a.queries.leaf_positions.len(), t); - assert_eq!(red_a.batching.alpha.len(), log_n); - - assert_eq!(red_b.pesat.mus.len(), l1); - assert_eq!(red_b.ood.answers.len(), s); - assert_eq!(red_b.queries.evaluation_points.len(), t); - - // Proximity proof string: per-query-row size = (l2 + l1) and - // outer length = t. - assert_eq!(red_a.proximity_proof.shift_query_answers.len(), t); - assert_eq!( - red_a.proximity_proof.shift_query_answers[0].len(), - config.l2 + l1 - ); - assert_eq!(red_a.proximity_proof.auth_j.len(), config.l2); - - // Independent randomness across calls => reductions differ. - assert_ne!(red_a.pesat.mus, red_b.pesat.mus); - assert_ne!(red_a.eta, red_b.eta); - assert_ne!(red_a.batching_mu, red_b.batching_mu); -} From b2b4700935279fd71fd4e7beff57b0388f0ae3da Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 12 May 2026 13:38:03 +0200 Subject: [PATCH 53/68] ior: collapse 9-type IOR trait to (Input, Output) per side; IOR is the star MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the IOR trait's formal Statement / Witness / ProverInputs / ReductionInputs / ReducedStatement / ProofString / ReducedWitness / VerifierInputs / VerifierOutputs split with a single ProverInput + ProverOutput and VerifierInput + VerifierOutput pair, plus a NAME constant. Removes reduce_statement from the trait (each IOR's implementation preserves the single-source-of-truth pattern via a private helper where the reduction is non-trivial — e.g., TwinConstraint). The new shape is the user-facing primary abstraction. No wrapper trait, no Step, no Pipeline. Per-IOR types follow a uniform port convention: ProverOutput { reduced, carry, [proof] } VerifierOutput { reduced, carry } with: - `reduced` — public reduced claim (same type on both sides; both endpoints recompute via shared private helper) - `carry` — endpoint-local state needed by later IORs (empty fields use `()` type for uniformity) - `proof` — only present where the IOR produces proof material (Proximity) Bridge and SampleQueries become first-class IORs (trivial cases of the shape) so the orchestrator stays IOR-uniform end to end. WARP::prove and WARP::verify are now a destructured choreography of seven IOR calls — each block names the IOR, its input fields, and its output ports at the call site: let PesatProverOutput { reduced: PesatReduced { mus, taus }, carry: PesatProverCarry { codewords, td_0 }, } = pesat_ior.prove(prover_state, PesatProverInput { l1, log_m, witnesses: &witnesses })?; Reading lib.rs is now reading the WARP protocol map. Renames `src/protocol/phases/` → `src/protocol/iors/` and the `BatchingStatement::from_phase_outputs` constructor → `from_ior_outputs` to drop the residual "phase" vocabulary (paper-section comments like "IOR 1: PESAT" remain — those are paper alignment, not internal terms). Tests transcript-identical: 22 lib + 2 integration + 9 verifier-negative all pass. --- src/lib.rs | 420 ++++++++---------- src/protocol/{phases => iors}/batching.rs | 212 ++++----- src/protocol/{phases => iors}/bridge.rs | 193 ++++---- src/protocol/iors/mod.rs | 88 ++++ src/protocol/iors/ood.rs | 147 ++++++ .../{phases => iors}/oracle_handle.rs | 0 src/protocol/iors/pesat.rs | 189 ++++++++ src/protocol/iors/proximity.rs | 313 +++++++++++++ src/protocol/iors/sample_queries.rs | 128 ++++++ .../{phases => iors}/twin_constraint.rs | 369 +++++++-------- src/protocol/mod.rs | 2 +- src/protocol/phases/mod.rs | 184 -------- src/protocol/phases/ood.rs | 167 ------- src/protocol/phases/pesat.rs | 209 --------- src/protocol/phases/proximity.rs | 269 ----------- src/protocol/phases/sample_queries.rs | 135 ------ 16 files changed, 1431 insertions(+), 1594 deletions(-) rename src/protocol/{phases => iors}/batching.rs (60%) rename src/protocol/{phases => iors}/bridge.rs (50%) create mode 100644 src/protocol/iors/mod.rs create mode 100644 src/protocol/iors/ood.rs rename src/protocol/{phases => iors}/oracle_handle.rs (100%) create mode 100644 src/protocol/iors/pesat.rs create mode 100644 src/protocol/iors/proximity.rs create mode 100644 src/protocol/iors/sample_queries.rs rename src/protocol/{phases => iors}/twin_constraint.rs (62%) delete mode 100644 src/protocol/phases/mod.rs delete mode 100644 src/protocol/phases/ood.rs delete mode 100644 src/protocol/phases/pesat.rs delete mode 100644 src/protocol/phases/proximity.rs delete mode 100644 src/protocol/phases/sample_queries.rs diff --git a/src/lib.rs b/src/lib.rs index 1670530..b7f2152 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,29 +47,32 @@ pub mod prelude { use crate::crypto::merkle::warp_scheme; use error::{DeciderError, ProverError, VerifierError}; -use protocol::phases::{ +use protocol::iors::{ batching::{ - Batching, BatchingProverInputs, BatchingReducedStatement, BatchingReducedWitness, - BatchingStatement, BatchingVerifierInputs, + Batching, BatchingProverCarry, BatchingProverInput, BatchingProverOutput, BatchingReduced, + BatchingVerifierInput, BatchingVerifierOutput, }, bridge::{ - Bridge, BridgeProverInputs, BridgeReducedStatement, BridgeReducedWitness, BridgeStatement, - BridgeVerifierInputs, BridgeWitness, + Bridge, BridgeProverCarry, BridgeProverInput, BridgeProverOutput, BridgeReduced, + BridgeVerifierInput, BridgeVerifierOutput, }, - ood::{Ood, OodProverInputs, OodReducedStatement, OodStatement}, - oracle_handle::MerkleIndexedOracle, + ood::{Ood, OodProverInput, OodProverOutput, OodReduced, OodVerifierInput, OodVerifierOutput}, pesat::{ - Pesat, PesatReducedStatement, PesatReducedWitness, PesatStatement, PesatVerifierOutputs, - PesatWitness, + Pesat, PesatProverCarry, PesatProverInput, PesatProverOutput, PesatReduced, + PesatVerifierCarry, PesatVerifierInput, PesatVerifierOutput, }, proximity::{ - Proximity, ProximityProofString, ProximityProverInputs, ProximityStatement, - ProximityVerifierInputs, + Proximity, ProximityProof, ProximityProverInput, ProximityProverOutput, + ProximityVerifierInput, ProximityVerifierOutput, + }, + sample_queries::{ + SampleQueries, SampleQueriesProverInput, SampleQueriesProverOutput, SampleQueriesReduced, + SampleQueriesVerifierInput, SampleQueriesVerifierOutput, }, - sample_queries::{SampleQueries, SampleQueriesReducedStatement, SampleQueriesStatement}, twin_constraint::{ - TwinConstraint, TwinConstraintProverInputs, TwinConstraintReducedStatement, - TwinConstraintReducedWitness, TwinConstraintStatement, TwinConstraintWitness, + TwinConstraint, TwinConstraintProverCarry, TwinConstraintProverInput, + TwinConstraintProverOutput, TwinConstraintReduced, TwinConstraintVerifierInput, + TwinConstraintVerifierOutput, }, IOR, }; @@ -192,157 +195,150 @@ where } = acc_witness; let acc_fs: Vec> = acc_tds.iter().map(|td| td.codewords()[0].clone()).collect(); - // Phase structs (held by value, borrow setup data for &self lifetime). - let pesat_phase = Pesat:: { + // IOR instances (stateless or borrowing &self.params for the &self lifetime). + let pesat_ior = Pesat:: { code: &self.params.code, hasher: &self.params.hasher, _phantom: PhantomData, }; - let tc_phase = TwinConstraint:: { + let twin_constraint_ior = TwinConstraint:: { r1cs: self.params.p.constraints(), _phantom: PhantomData, }; - let bridge_phase = Bridge::::new(); - let ood_phase = Ood::::new(); - let sample_queries_phase = SampleQueries::::new(); - let batching_phase = Batching::::new(); - let proximity_phase = Proximity:: { + let bridge_ior = Bridge::::new(); + let ood_ior = Ood::::new(); + let sample_queries_ior = SampleQueries::::new(); + let batching_ior = Batching::::new(); + let proximity_ior = Proximity:: { hasher: &self.params.hasher, _phantom: PhantomData, }; - // ── Phase 2: PESAT — encode witnesses, commit, derive τ ────── - let (PesatReducedStatement { mus, taus }, _, PesatReducedWitness { codewords, td_0 }) = - pesat_phase.prove( - prover_state, - &PesatStatement { l1, log_m }, - &PesatWitness { - witnesses: &witnesses, - }, - &(), - )?; - - // ── Phase 3a: TwinConstraint — fused-fold sumcheck ─────────── - let ( - TwinConstraintReducedStatement { - gamma: _, - zeta_0, - beta_tau, - deferred: _, - }, - _, - TwinConstraintReducedWitness { f, z }, - ) = tc_phase.prove( + // ── IOR 1: PESAT — encode witnesses, commit, derive τ ──────── + let PesatProverOutput { + reduced: PesatReduced { mus, taus }, + carry: PesatProverCarry { codewords, td_0 }, + } = pesat_ior.prove( prover_state, - &TwinConstraintStatement { - acc_instance, - l1_mus: mus.clone(), - l1_taus: taus, - log_l, + PesatProverInput { + l1, log_m, - log_n, - }, - &TwinConstraintWitness { - acc_witness_w: &acc_ws, - instances: &instances, witnesses: &witnesses, }, - &TwinConstraintProverInputs { + )?; + + // ── IOR 2: TwinConstraint — fused-fold sumcheck ────────────── + let TwinConstraintProverOutput { + reduced: + TwinConstraintReduced { + gamma: _, + zeta_0, + beta_tau, + deferred: _, + }, + carry: TwinConstraintProverCarry { f, z }, + } = twin_constraint_ior.prove( + prover_state, + TwinConstraintProverInput { + acc_instance, + l1_mus: &mus, + l1_taus: &taus, + witnesses: &witnesses, + instances: &instances, + acc_witness_w: &acc_ws, fresh_codewords: &codewords, acc_codewords: &acc_fs, + log_l, + log_m, + log_n, }, )?; - // ── Phase 3b: Bridge — publish (td_new, η, ν₀); split z ────── - let ( - BridgeReducedStatement { - eta, - nu_0, - td_new_root: _, - }, - _, - BridgeReducedWitness { - td_new, - new_x, - new_w, - }, - ) = bridge_phase.prove( + // ── IOR 3: Bridge — publish (td_new, η, ν₀); split z ───────── + let BridgeProverOutput { + reduced: + BridgeReduced { + eta, + nu_0, + td_new_root: _, + }, + carry: BridgeProverCarry { td_new, new_x, new_w }, + } = bridge_ior.prove( prover_state, - &BridgeStatement { - zeta_0: zeta_0.clone(), - beta_tau: beta_tau.clone(), - log_m, - n_minus_k: N - k, - }, - &BridgeWitness { z: &z, f: &f }, - &BridgeProverInputs { + BridgeProverInput { + zeta_0: &zeta_0, + beta_tau: &beta_tau, + z: &z, + f: &f, bundled_pesat: &self.params.p, hasher: &self.params.hasher, code_len: n, - _f: PhantomData, + log_m, + n_minus_k: N - k, }, )?; - // ── Phase 3c: OOD — out-of-domain queries ──────────────────── - let (OodReducedStatement { samples_flat, answers }, _, _) = ood_phase.prove( + // ── IOR 4: OOD — query reduced oracle out of domain ────────── + let OodProverOutput { + reduced: OodReduced { samples_flat, answers }, + carry: (), + } = ood_ior.prove( prover_state, - &OodStatement { + OodProverInput { + oracle: &f, s: self.params.config.s, log_n, }, - &(), - &OodProverInputs { oracle: &f }, )?; - // ── Phase 3c → 3d: SampleQueries — sample t shift positions ── - let (SampleQueriesReducedStatement { queries }, _, _) = sample_queries_phase.prove( + // ── IOR 5: SampleQueries — sample t shift positions ────────── + let SampleQueriesProverOutput { + reduced: SampleQueriesReduced { queries }, + carry: (), + } = sample_queries_ior.prove( prover_state, - &SampleQueriesStatement { + SampleQueriesProverInput { log_n, t: self.params.config.t, }, - &(), - &(), )?; - // ── Phase 3d: Batching — DAG fan-in over (ζ₀, OOD, queries) ── - let (BatchingReducedStatement { alpha }, _, BatchingReducedWitness { mu }) = batching_phase - .prove( - prover_state, - &BatchingStatement::from_phase_outputs( - zeta_0.clone(), - &samples_flat, - &queries.evaluation_points, - self.params.config.s, - self.params.config.t, - log_n, - ), - &(), - &BatchingProverInputs { oracle: &f }, - )?; - - // ── Phase 3e: Proximity — shift-query openings ─────────────── - let ( - _, - ProximityProofString { + // ── IOR 6: Batching — DAG fan-in over (ζ₀, OOD, queries) ───── + let BatchingProverOutput { + reduced: BatchingReduced { alpha }, + carry: BatchingProverCarry { mu }, + } = batching_ior.prove( + prover_state, + BatchingProverInput { + zeta_0: &zeta_0, + samples_flat: &samples_flat, + query_eval_points: &queries.evaluation_points, + oracle: &f, + s: self.params.config.s, + t: self.params.config.t, + log_n, + }, + )?; + + // ── IOR 7: Proximity — open committed oracles at shift positions ── + let ProximityProverOutput { + reduced: (), + carry: (), + proof: ProximityProof { auth_0, auth_j, shift_query_answers, }, - _, - ) = proximity_phase.prove( + } = proximity_ior.prove( prover_state, - &ProximityStatement { - queries: queries.clone(), + ProximityProverInput { + queries: &queries, + td_0: &td_0, + acc_td: &acc_tds, l2, t: self.params.config.t, n, }, - &(), - &ProximityProverInputs { - td_0: &td_0, - acc_td: &acc_tds, - }, )?; // ── Assemble new accumulator state and proof ───────────────── @@ -411,164 +407,113 @@ where // statement — Proximity needs them for the per-acc Merkle handles. let l2_roots = parsed_acc.rt.clone(); - // Phase structs (held by value, borrow setup data for &self lifetime). - let pesat_phase = Pesat:: { + // IOR instances. + let pesat_ior = Pesat:: { code: &self.params.code, hasher: &self.params.hasher, _phantom: PhantomData, }; - let tc_phase = TwinConstraint:: { + let twin_constraint_ior = TwinConstraint:: { r1cs: self.params.p.constraints(), _phantom: PhantomData, }; - let bridge_phase = Bridge::::new(); - let ood_phase = Ood::::new(); - let sample_queries_phase = SampleQueries::::new(); - let batching_phase = Batching::::new(); - let proximity_phase = Proximity:: { + let bridge_ior = Bridge::::new(); + let ood_ior = Ood::::new(); + let sample_queries_ior = SampleQueries::::new(); + let batching_ior = Batching::::new(); + let proximity_ior = Proximity:: { hasher: &self.params.hasher, _phantom: PhantomData, }; - // ── Phase 2: PESAT::verify ────────────────────────────────── - let ( - PesatReducedStatement { + // ── IOR 1: PESAT — read commitment and (μ, τ) ──────────────── + let PesatVerifierOutput { + reduced: PesatReduced { mus: l1_mus, taus: l1_taus, }, - PesatVerifierOutputs { rt_0 }, - ) = pesat_phase.verify(verifier_state, &PesatStatement { l1, log_m }, &())?; - - // ── Phase 3a: TwinConstraint::verify ──────────────────────── - let ( - TwinConstraintReducedStatement { - gamma, - zeta_0, - beta_tau, - deferred, - }, - _, - ) = tc_phase.verify( + carry: PesatVerifierCarry { rt_0 }, + } = pesat_ior.verify(verifier_state, PesatVerifierInput { l1, log_m })?; + + // ── IOR 2: TwinConstraint::verify ──────────────────────────── + let TwinConstraintVerifierOutput { + reduced: + TwinConstraintReduced { + gamma, + zeta_0, + beta_tau: _, + deferred, + }, + carry: (), + } = twin_constraint_ior.verify( verifier_state, - &TwinConstraintStatement { - acc_instance: parsed_acc, - l1_mus: l1_mus.clone(), - l1_taus: l1_taus.clone(), + TwinConstraintVerifierInput { + parsed_acc, + l1_mus: &l1_mus, + l1_taus: &l1_taus, log_l, log_m, log_n, }, - &(), )?; - // ── Phase 3b: Bridge::verify — read (td, η, ν₀), discharge TC's deferred check ─ - let ( - BridgeReducedStatement { + // ── IOR 3: Bridge — read (td, η, ν₀); discharge TC's deferred ─ + let BridgeVerifierOutput { + reduced: BridgeReduced { eta: _, nu_0, td_new_root: _, }, - _, - ) = bridge_phase.verify( + carry: (), + } = bridge_ior.verify( verifier_state, - &BridgeStatement { - zeta_0: zeta_0.clone(), - beta_tau, - log_m, - n_minus_k: N - k, - }, - &BridgeVerifierInputs { - deferred: &deferred, + BridgeVerifierInput { gamma: &gamma, + deferred: &deferred, }, )?; - // ── Phase 3c: OOD::verify ─────────────────────────────────── - let (OodReducedStatement { samples_flat, answers }, _) = ood_phase.verify( + // ── IOR 4: OOD::verify ────────────────────────────────────── + let OodVerifierOutput { + reduced: OodReduced { samples_flat, answers }, + carry: (), + } = ood_ior.verify( verifier_state, - &OodStatement { + OodVerifierInput { s: self.params.config.s, log_n, }, - &(), )?; - // ── Phase 3c → 3d: SampleQueries::verify ──────────────────── - let (SampleQueriesReducedStatement { queries }, _) = sample_queries_phase.verify( + // ── IOR 5: SampleQueries::verify ──────────────────────────── + let SampleQueriesVerifierOutput { + reduced: SampleQueriesReduced { queries }, + carry: (), + } = sample_queries_ior.verify( verifier_state, - &SampleQueriesStatement { + SampleQueriesVerifierInput { log_n, t: self.params.config.t, }, - &(), )?; - // ── Whole-proof arity checks (orchestrator-owned) ──────────── - (proof.shift_query_answers.len() == self.params.config.t) - .then_some(()) - .ok_or(VerifierError::NumShiftQueries)?; - (proof.auth_j.len() == l2) - .then_some(()) - .ok_or(VerifierError::NumL2Instances)?; - - // ── Build IndexedOracle handles for Proximity ─────────────── - let mut indexed: Vec<(usize, usize)> = queries - .leaf_positions - .iter() - .copied() - .enumerate() - .map(|(row, pos)| (pos, row)) - .collect(); - indexed.sort_by_key(|&(pos, _)| pos); - indexed.dedup_by_key(|&mut (pos, _)| pos); - let sorted_unique: Vec = indexed.iter().map(|&(p, _)| p).collect(); - let row_indices: Vec = indexed.iter().map(|&(_, r)| r).collect(); - - let fresh_values: Vec> = row_indices - .iter() - .map(|&r| proof.shift_query_answers[r][l2..].to_vec()) - .collect(); - let fresh_handle = MerkleIndexedOracle::new( - warp_scheme::(self.params.hasher.clone(), n), - &rt_0, - &proof.auth_0, - sorted_unique.clone(), - fresh_values, - ); - - let acc_handles: Vec> = (0..l2) - .map(|j| { - let acc_values: Vec> = row_indices - .iter() - .map(|&r| vec![proof.shift_query_answers[r][j]]) - .collect(); - MerkleIndexedOracle::new( - warp_scheme::(self.params.hasher.clone(), n), - &l2_roots[j], - &proof.auth_j[j], - sorted_unique.clone(), - acc_values, - ) - }) - .collect(); - - // ── Phase 3e: Proximity::verify ───────────────────────────── - proximity_phase.verify( + // ── IOR 7: Proximity::verify (arity + handle building lives here) ── + let ProximityVerifierOutput { reduced: (), carry: () } = proximity_ior.verify( verifier_state, - &ProximityStatement { - queries: queries.clone(), + ProximityVerifierInput { + queries: &queries, + rt_0: &rt_0, + l2_roots: &l2_roots, + auth_0: &proof.auth_0, + auth_j: &proof.auth_j, + shift_query_answers: &proof.shift_query_answers, l2, t: self.params.config.t, n, }, - &ProximityVerifierInputs { - fresh: &fresh_handle, - acc: &acc_handles, - _f: PhantomData, - }, )?; - // ── Recompute ν vector from shift answers ────────────────── + // ── Recompute ν vector from shift answers (orchestrator-owned) ── let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma); let mut nus = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); nus.push(nu_0); @@ -581,20 +526,21 @@ where nus.push(nu_st); } - // ── Phase 3d: Batching::verify ───────────────────────────── - let (BatchingReducedStatement { alpha }, _) = batching_phase.verify( + // ── IOR 6: Batching::verify ───────────────────────────────── + let BatchingVerifierOutput { + reduced: BatchingReduced { alpha }, + carry: (), + } = batching_ior.verify( verifier_state, - &BatchingStatement::from_phase_outputs( - zeta_0.clone(), - &samples_flat, - &queries.evaluation_points, - self.params.config.s, - self.params.config.t, - log_n, - ), - &BatchingVerifierInputs { + BatchingVerifierInput { + zeta_0: &zeta_0, + samples_flat: &samples_flat, + query_eval_points: &queries.evaluation_points, nus, acc_mu: acc_mu_first, + s: self.params.config.s, + t: self.params.config.t, + log_n, }, )?; diff --git a/src/protocol/phases/batching.rs b/src/protocol/iors/batching.rs similarity index 60% rename from src/protocol/phases/batching.rs rename to src/protocol/iors/batching.rs index d0a0a71..d162919 100644 --- a/src/protocol/phases/batching.rs +++ b/src/protocol/iors/batching.rs @@ -11,19 +11,14 @@ //! sumcheck, with the CBBZ23 / HyperPlonk sparse-evaluation optimization //! (`accumulate_sparse_evaluations`) folded in. //! -//! IOR signature -//! ------------- -//! - `Statement` — `(zetas_prefix, s, t, log_n)` — shared. -//! - `Witness` — `()` -//! - `ProverInputs` — `&Oracle` (the committed oracle, full data) -//! - `VerifierInputs` — `(nus, acc_mu)` — used to compute `σ₂` and the -//! final-claim oracle check. -//! - `ReductionInputs` — `alpha` (the LSB sumcheck challenges); both sides -//! compute and feed it through `reduce_statement`. -//! - `ReducedStatement` — `alpha` — the new code-eval point (LSB-indexed) -//! - `ProofString` — `()` -//! - `ReducedWitness` — `mu` — the prover's reported `\hat f(α)` -//! - `VerifierOutputs` — `()` +//! IOR ports +//! --------- +//! - input (prover): `{ zeta_0, samples_flat, query_eval_points, oracle, s, t, log_n }` +//! - input (verifier): `{ zeta_0, samples_flat, query_eval_points, nus, acc_mu, s, t, log_n }` +//! - `reduced`: `{ alpha }` — same on both sides (verifier derives `alpha` +//! from the transcript) +//! - `carry` (prover): `{ mu }` — the prover's reported `\hat f(α)` +//! - verifier has no carry use ark_ff::{Field, PrimeField}; use ark_std::log2; @@ -37,8 +32,7 @@ use std::marker::PhantomData; use crate::count_ops; use crate::error::{ProverError, VerifierError}; -use crate::protocol::oracle::Oracle; -use crate::protocol::phases::IOR; +use crate::protocol::iors::IOR; use crate::protocol::transcript::EffscVerifierTranscript; use crate::utils::poly::{eq_poly, eq_poly_non_binary}; @@ -84,24 +78,23 @@ fn batched_constraint_poly( result } -// ─── IOR signature types ────────────────────────────────────────────────── +// ─── Statement helper ───────────────────────────────────────────────────── -pub struct BatchingStatement { +/// Internal layout of the `1 + s + t` evaluation points used by the +/// batching sumcheck. Constructed inside `prove` / `verify` from the +/// `(zeta_0, samples_flat, query_eval_points, s, t, log_n)` tuple — single +/// source of truth so the two sides cannot drift. +struct BatchingStatement { /// `1 + s + t` evaluation points: `[ζ_0, ood_j…, query_k…]`. - pub zetas_prefix: Vec>, - pub s: usize, - pub t: usize, - pub log_n: usize, - pub _phantom: std::marker::PhantomData, + zetas_prefix: Vec>, + s: usize, + t: usize, + log_n: usize, } impl BatchingStatement { - /// Single source of truth for the `zetas_prefix` shape — both the - /// prover and verifier orchestrators construct their `BatchingStatement` - /// through this. Drift between sides becomes structurally impossible. - /// /// Layout: `[ζ_0, ood_chunk_0, …, ood_chunk_{s-1}, query_0, …, query_{t-1}]`. - pub fn from_phase_outputs( + fn from_ior_outputs( zeta_0: Vec, ood_samples_flat: &[F], query_eval_points: &[Vec], @@ -122,37 +115,60 @@ impl BatchingStatement { s, t, log_n, - _phantom: std::marker::PhantomData, } } } -pub struct BatchingProverInputs<'a, F: Field> { - pub oracle: &'a Oracle, +// ─── Inputs ─────────────────────────────────────────────────────────────── + +pub struct BatchingProverInput<'a, F: Field> { + pub zeta_0: &'a [F], + pub samples_flat: &'a [F], + pub query_eval_points: &'a [Vec], + pub oracle: &'a crate::protocol::oracle::Oracle, + pub s: usize, + pub t: usize, + pub log_n: usize, } -pub struct BatchingVerifierInputs { +pub struct BatchingVerifierInput<'a, F: Field> { + pub zeta_0: &'a [F], + pub samples_flat: &'a [F], + pub query_eval_points: &'a [Vec], /// `1 + s + t` ν values; used to compute `σ₂ = Σ ξ_eq · ν`. pub nus: Vec, /// Multiplier on the final-claim oracle check. pub acc_mu: F, + pub s: usize, + pub t: usize, + pub log_n: usize, } -pub struct BatchingReductionInputs { - /// LSB-indexed sumcheck challenge vector. - pub alpha: Vec, -} +// ─── Output ports ───────────────────────────────────────────────────────── -pub struct BatchingReducedStatement { +/// Public reduced claim — same on both sides. +pub struct BatchingReduced { /// New code-eval point (LSB-indexed). pub alpha: Vec, } -pub struct BatchingReducedWitness { +pub struct BatchingProverCarry { /// `\hat f(α)` — prover's report. pub mu: F, } +pub struct BatchingProverOutput { + pub reduced: BatchingReduced, + pub carry: BatchingProverCarry, +} + +pub struct BatchingVerifierOutput { + pub reduced: BatchingReduced, + pub carry: (), +} + +// ─── IOR ────────────────────────────────────────────────────────────────── + /// Batching phase configuration. pub struct Batching<'a, F: Field> { pub _phantom: PhantomData<&'a F>, @@ -176,63 +192,47 @@ impl<'a, F> IOR for Batching<'a, F> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { - type Statement = BatchingStatement; - type Witness<'b> - = () - where - Self: 'b; - type ProverInputs<'b> - = BatchingProverInputs<'b, F> + const NAME: &'static str = "Batching"; + + type ProverInput<'b> + = BatchingProverInput<'b, F> where Self: 'b; - type VerifierInputs<'b> - = BatchingVerifierInputs + type ProverOutput = BatchingProverOutput; + type VerifierInput<'b> + = BatchingVerifierInput<'b, F> where Self: 'b; - type ReductionInputs = BatchingReductionInputs; - type ReducedStatement = BatchingReducedStatement; - type ProofString = (); - type ReducedWitness = BatchingReducedWitness; - type VerifierOutputs = (); - - fn reduce_statement( - &self, - _statement: &Self::Statement, - inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement { - BatchingReducedStatement { - alpha: inputs.alpha.clone(), - } - } + type VerifierOutput = BatchingVerifierOutput; #[tracing::instrument( name = "batching", skip_all, - fields(s = statement.s, t = statement.t, log_n = statement.log_n) + fields(s = input.s, t = input.t, log_n = input.log_n) )] - fn prove_inner<'b>( + fn prove<'b>( &self, - prover_state: &mut ProverState, - statement: &Self::Statement, - _witness: &Self::Witness<'b>, - inputs: &Self::ProverInputs<'b>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > + transcript: &mut ProverState, + input: Self::ProverInput<'b>, + ) -> Result where - 'a: 'b, + Self: 'b, { - let n = inputs.oracle.len(); + let statement = BatchingStatement::from_ior_outputs( + input.zeta_0.to_vec(), + input.samples_flat, + input.query_eval_points, + input.s, + input.t, + input.log_n, + ); + + let n = input.oracle.len(); let r = 1 + statement.s + statement.t; let log_r = log2(r) as usize; debug_assert_eq!(statement.zetas_prefix.len(), r); - let xis = prover_state.verifier_messages_vec::(log_r); + let xis = transcript.verifier_messages_vec::(log_r); let (xi_eq_evals, ood_evals_vec) = { let _s = tracing::info_span!("batching.eq_evals").entered(); @@ -258,60 +258,67 @@ where let log_n_bits = ark_std::log2(n) as u64; count_ops!(BatchingRounds, log_n_bits); let mut ip = InnerProductProver::new( - inputs.oracle.evals().to_vec(), + input.oracle.evals().to_vec(), batched_constraint_poly(&ood_evals_vec, &id_non_0_eval_sums), ); let mut challenges = - sumcheck(&mut ip, log_n_bits as usize, prover_state, noop_hook).challenges; + sumcheck(&mut ip, log_n_bits as usize, transcript, noop_hook).challenges; challenges.reverse(); challenges }; - let mu = inputs.oracle.query_at_point(&alpha); + let mu = input.oracle.query_at_point(&alpha); - Ok(( - BatchingReductionInputs { + Ok(BatchingProverOutput { + reduced: BatchingReduced { alpha: alpha.clone(), }, - (), - BatchingReducedWitness { mu }, - )) + carry: BatchingProverCarry { mu }, + }) } #[tracing::instrument( name = "batching.verify", skip_all, - fields(s = statement.s, t = statement.t, log_n = statement.log_n) + fields(s = input.s, t = input.t, log_n = input.log_n) )] - fn verify_inner<'b, 'c>( + fn verify<'b, 'v>( &self, - verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement, - inputs: &Self::VerifierInputs<'c>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + transcript: &mut VerifierState<'v>, + input: Self::VerifierInput<'b>, + ) -> Result where - 'a: 'c, + Self: 'b, { + let statement = BatchingStatement::from_ior_outputs( + input.zeta_0.to_vec(), + input.samples_flat, + input.query_eval_points, + input.s, + input.t, + input.log_n, + ); + let r = 1 + statement.s + statement.t; let log_r = log2(r) as usize; debug_assert_eq!(statement.zetas_prefix.len(), r); - debug_assert_eq!(inputs.nus.len(), r); + debug_assert_eq!(input.nus.len(), r); // Squeeze ξ matching the prover. let xis: Vec = (0..log_r) - .map(|_| verifier_state.verifier_message::()) + .map(|_| transcript.verifier_message::()) .collect(); let xi_eq_evals = (0..r).map(|i| eq_poly(&xis, i)).collect::>(); // σ₂ = Σ ξ_eq · ν. let sigma_2 = xi_eq_evals .iter() - .zip(&inputs.nus) + .zip(&input.nus) .fold(F::zero(), |acc, (xi_eq, nu)| acc + *xi_eq * nu); // Run sumcheck_verify and check the final-claim oracle check. let res = { - let mut wrap = EffscVerifierTranscript(verifier_state); + let mut wrap = EffscVerifierTranscript(transcript); sumcheck_verify(sigma_2, 2, statement.log_n, &mut wrap, |_, _| Ok(()))? }; let alpha_lsb: Vec = res.challenges.iter().rev().copied().collect(); @@ -320,13 +327,18 @@ where for zeta in &statement.zetas_prefix { zeta_eqs.push(eq_poly_non_binary(zeta, &alpha_lsb)); } - let expected = inputs.acc_mu + let expected = input.acc_mu * zeta_eqs .into_iter() .zip(&xi_eq_evals) .fold(F::zero(), |acc, (a, b)| acc + a * *b); - (expected == res.final_claim).then_some(()).ok_or(VerifierError::Target)?; + (expected == res.final_claim) + .then_some(()) + .ok_or(VerifierError::Target)?; - Ok((BatchingReductionInputs { alpha: alpha_lsb }, ())) + Ok(BatchingVerifierOutput { + reduced: BatchingReduced { alpha: alpha_lsb }, + carry: (), + }) } } diff --git a/src/protocol/phases/bridge.rs b/src/protocol/iors/bridge.rs similarity index 50% rename from src/protocol/phases/bridge.rs rename to src/protocol/iors/bridge.rs index 07c5d05..0d2c6c8 100644 --- a/src/protocol/phases/bridge.rs +++ b/src/protocol/iors/bridge.rs @@ -21,20 +21,18 @@ //! //! Modeled as an IOR for uniformity with the rest of the choreography, //! even though it has no verifier challenges — Bridge is "0-round, -//! prover sends three values, verifier checks deferred." Same shape as -//! the IOR trait, just with degenerate fields where they don't apply. +//! prover sends three values, verifier checks deferred." //! -//! IOR signature -//! ------------- -//! - `Statement` — `(ζ₀, β_τ, log_m, n_minus_k)` -//! - `Witness` — `(z, f)` — prover's TC-side reduced state, borrowed -//! - `ProverInputs` — `(bundled_pesat, hasher, code_len)` — config refs -//! - `VerifierInputs` — `(deferred, γ)` — what's needed to discharge TC -//! - `ReductionInputs` — `(η, ν₀, td_new_root)` — transcript-published values -//! - `ReducedStatement` — same as `ReductionInputs` (the publish is the reduction) -//! - `ProofString` — `()` (everything goes through transcript writes / reads) -//! - `ReducedWitness` — `(td_new, new_x, new_w)` — fed into the new accumulator -//! - `VerifierOutputs` — `()` +//! IOR ports +//! --------- +//! - input (prover): `{ zeta_0, beta_tau, z, f, bundled_pesat, hasher, +//! code_len, log_m, n_minus_k }` +//! - input (verifier): `{ gamma, deferred }` +//! - `reduced`: `{ eta, nu_0, td_new_root }` — same on both sides +//! - `carry` (prover): `{ td_new, new_x, new_w }` — feeds the next +//! accumulator +//! - verifier has no carry — `discharge` consumes the deferred check +//! in-place use ark_ff::Field; use ark_mt::MerkleHasher; @@ -46,52 +44,44 @@ use crate::count_ops; use crate::crypto::merkle::{warp_scheme, WarpCommitted}; use crate::error::{ProverError, VerifierError}; use crate::protocol::oracle::Oracle; -use crate::protocol::phases::twin_constraint::DeferredOracleCheck; -use crate::protocol::phases::IOR; +use crate::protocol::iors::twin_constraint::DeferredOracleCheck; +use crate::protocol::iors::IOR; use crate::relations::BundledPESAT; -pub struct BridgeStatement { - pub zeta_0: Vec, - pub beta_tau: Vec, - pub log_m: usize, - pub n_minus_k: usize, -} +// ─── Inputs ─────────────────────────────────────────────────────────────── -pub struct BridgeWitness<'a, F: Field> { - pub z: &'a [F], - pub f: &'a Oracle, -} - -pub struct BridgeProverInputs<'a, F, P, H> +pub struct BridgeProverInput<'a, F, P, H> where F: Field, P: BundledPESAT, H: MerkleHasher>, { + pub zeta_0: &'a [F], + pub beta_tau: &'a [F], + pub z: &'a [F], + pub f: &'a Oracle, pub bundled_pesat: &'a P, pub hasher: &'a H, pub code_len: usize, - pub _f: PhantomData, + pub log_m: usize, + pub n_minus_k: usize, } -pub struct BridgeVerifierInputs<'a, F: Field> { - pub deferred: &'a DeferredOracleCheck, +pub struct BridgeVerifierInput<'a, F: Field> { pub gamma: &'a [F], + pub deferred: &'a DeferredOracleCheck, } -pub struct BridgeReductionInputs { - pub eta: F, - pub nu_0: F, - pub td_new_root: H::Digest, -} +// ─── Output ports ───────────────────────────────────────────────────────── -pub struct BridgeReducedStatement { +/// Public reduced claim — same on both sides. +pub struct BridgeReduced { pub eta: F, pub nu_0: F, pub td_new_root: H::Digest, } -pub struct BridgeReducedWitness +pub struct BridgeProverCarry where F: Field, H: MerkleHasher>, @@ -101,9 +91,25 @@ where pub new_w: Vec, } +pub struct BridgeProverOutput +where + F: Field, + H: MerkleHasher>, +{ + pub reduced: BridgeReduced, + pub carry: BridgeProverCarry, +} + +pub struct BridgeVerifierOutput { + pub reduced: BridgeReduced, + pub carry: (), +} + +// ─── IOR ────────────────────────────────────────────────────────────────── + /// TC → OOD bridge phase configuration. Stateless; the lifetime -/// parameter exists only to anchor `ProverInputs<'a>` / `Witness<'a>` -/// for the IOR trait impl. +/// parameter exists only to anchor `ProverInput<'a>` for the IOR trait +/// impl. pub struct Bridge<'a, F, P, H> where F: Field, @@ -144,71 +150,44 @@ where H: MerkleHasher>, H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize + Clone, { - type Statement = BridgeStatement; - type Witness<'b> - = BridgeWitness<'b, F> - where - Self: 'b; - type ProverInputs<'b> - = BridgeProverInputs<'b, F, P, H> + const NAME: &'static str = "Bridge"; + + type ProverInput<'b> + = BridgeProverInput<'b, F, P, H> where Self: 'b; - type VerifierInputs<'b> - = BridgeVerifierInputs<'b, F> + type ProverOutput = BridgeProverOutput; + type VerifierInput<'b> + = BridgeVerifierInput<'b, F> where Self: 'b; - type ReductionInputs = BridgeReductionInputs; - type ReducedStatement = BridgeReducedStatement; - type ProofString = (); - type ReducedWitness = BridgeReducedWitness; - type VerifierOutputs = (); - - fn reduce_statement( - &self, - _statement: &Self::Statement, - inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement { - BridgeReducedStatement { - eta: inputs.eta, - nu_0: inputs.nu_0, - td_new_root: inputs.td_new_root.clone(), - } - } + type VerifierOutput = BridgeVerifierOutput; #[tracing::instrument( name = "bridge", skip_all, - fields(log_m = statement.log_m, n_minus_k = statement.n_minus_k) + fields(log_m = input.log_m, n_minus_k = input.n_minus_k) )] - fn prove_inner<'b>( + fn prove<'b>( &self, - prover_state: &mut ProverState, - statement: &Self::Statement, - witness: &Self::Witness<'b>, - inputs: &Self::ProverInputs<'b>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > + transcript: &mut ProverState, + input: Self::ProverInput<'b>, + ) -> Result where Self: 'b, { // η = ⟨ eq(β_τ), p(z) ⟩ - let beta_eq_evals = compute_hypercube_eq_evals(statement.log_m, &statement.beta_tau); - let eta = inputs + let beta_eq_evals = compute_hypercube_eq_evals(input.log_m, input.beta_tau); + let eta = input .bundled_pesat - .evaluate_bundled(&beta_eq_evals, witness.z) + .evaluate_bundled(&beta_eq_evals, input.z) .map_err(|_| ProverError::SpongeFish)?; // ν₀ = f̂(ζ₀) - let nu_0 = witness.f.query_at_point(&statement.zeta_0); + let nu_0 = input.f.query_at_point(input.zeta_0); // (new_x, new_w) = z[..N-k], z[N-k..] - let (new_x_slice, new_w_slice) = witness.z.split_at(statement.n_minus_k); + let (new_x_slice, new_w_slice) = input.z.split_at(input.n_minus_k); let new_x = new_x_slice.to_vec(); let new_w = new_w_slice.to_vec(); @@ -216,56 +195,54 @@ where let td_new = { let _s = tracing::info_span!("bridge.commit_new_oracle").entered(); count_ops!(MerkleTreeBuilds); - let scheme = warp_scheme::(inputs.hasher.clone(), inputs.code_len); - scheme.commit(&[witness.f.evals().to_vec()]) + let scheme = warp_scheme::(input.hasher.clone(), input.code_len); + scheme.commit(&[input.f.evals().to_vec()]) }; let td_new_root = td_new.root().clone(); // Absorb (td_new.root, η, ν₀) - prover_state.prover_message(&td_new_root); - prover_state.prover_message(&eta); - prover_state.prover_message(&nu_0); + transcript.prover_message(&td_new_root); + transcript.prover_message(&eta); + transcript.prover_message(&nu_0); - Ok(( - BridgeReductionInputs { + Ok(BridgeProverOutput { + reduced: BridgeReduced { eta, nu_0, td_new_root, }, - (), - BridgeReducedWitness { + carry: BridgeProverCarry { td_new, new_x, new_w, }, - )) + }) } #[tracing::instrument(name = "bridge.verify", skip_all)] - fn verify_inner<'b, 'c>( + fn verify<'b, 'v>( &self, - verifier_state: &mut VerifierState<'b>, - _statement: &Self::Statement, - inputs: &Self::VerifierInputs<'c>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + transcript: &mut VerifierState<'v>, + input: Self::VerifierInput<'b>, + ) -> Result where - Self: 'c, + Self: 'b, { // Read (td_digest, η, ν₀) from the transcript. - let td_new_root: H::Digest = verifier_state.prover_message()?; - let eta: F = verifier_state.prover_message()?; - let nu_0: F = verifier_state.prover_message()?; + let td_new_root: H::Digest = transcript.prover_message()?; + let eta: F = transcript.prover_message()?; + let nu_0: F = transcript.prover_message()?; // Discharge TC's deferred check. - inputs.deferred.discharge(inputs.gamma, nu_0, eta)?; + input.deferred.discharge(input.gamma, nu_0, eta)?; - Ok(( - BridgeReductionInputs { + Ok(BridgeVerifierOutput { + reduced: BridgeReduced { eta, nu_0, td_new_root, }, - (), - )) + carry: (), + }) } } diff --git a/src/protocol/iors/mod.rs b/src/protocol/iors/mod.rs new file mode 100644 index 0000000..6cb2f0f --- /dev/null +++ b/src/protocol/iors/mod.rs @@ -0,0 +1,88 @@ +//! Warp IORs as first-class modules. +//! +//! Each submodule implements one Interactive Oracle Reduction from the +//! WARP construction. Every IOR has the same shape (a single typed +//! `ProverInput` and `VerifierInput`, returning a single typed +//! `ProverOutput` and `VerifierOutput`), so the orchestrator in +//! [`crate::WARP::prove`] / [`crate::WARP::verify`] is a destructured +//! choreography of IOR calls. +//! +//! Output structure convention: each `ProverOutput` / `VerifierOutput` +//! is built from named *ports*: +//! +//! - `reduced` — public reduced claim, identical on prover and verifier +//! sides (same type). Both endpoints derive their `reduced` from the +//! transcript reads. +//! - `carry` — endpoint-only local state needed by *later* IORs (prover +//! carry holds full oracle data, witnesses, commitments; verifier +//! carry holds parsed digests). +//! - `proof` — external proof material that the orchestrator collects +//! into the global `WARPProof`. Optional; many IORs have `()` here. +//! +//! Downstream IORs reference upstream ports explicitly by name in their +//! own `ProverInput` / `VerifierInput`. The WARP protocol is a DAG over +//! IORs, not a linear chain. + +pub mod batching; +pub mod bridge; +pub mod ood; +pub mod oracle_handle; +pub mod pesat; +pub mod proximity; +pub mod sample_queries; +pub mod twin_constraint; + +use spongefish::{ProverState, VerifierState}; + +use crate::error::{ProverError, VerifierError}; + +/// Interactive Oracle Reduction. +/// +/// The single abstraction in WARP's protocol layer. Each IOR exposes +/// `prove(transcript, input) -> output` and a symmetric `verify`. Both +/// sides return a typed `ProverOutput` / `VerifierOutput` whose fields +/// are named ports (`reduced`, `carry`, optionally `proof`) — the +/// caller destructures by name at the call site. +/// +/// Drift between prover and verifier sides is structural: the `reduced` +/// field on both outputs has the same type. Each IOR's implementation +/// is responsible for computing the same reduced value on both sides +/// (typically via a shared private helper inside its module). +pub trait IOR { + /// Human-readable identifier — matches the WARP paper's phase label. + const NAME: &'static str; + + /// Prover-side input. Bundles the public claim, prover-private + /// witness, and any upstream-IOR oracle data the prover consumes. + type ProverInput<'a> + where + Self: 'a; + + /// Prover-side output: `reduced` + `carry` + optional `proof`. + type ProverOutput; + + /// Verifier-side input. Bundles the public claim and any + /// upstream-IOR digests/handles the verifier consumes. + type VerifierInput<'a> + where + Self: 'a; + + /// Verifier-side output: `reduced` + `carry` + optional `proof`. + type VerifierOutput; + + fn prove<'a>( + &self, + transcript: &mut ProverState, + input: Self::ProverInput<'a>, + ) -> Result + where + Self: 'a; + + fn verify<'a, 'v>( + &self, + transcript: &mut VerifierState<'v>, + input: Self::VerifierInput<'a>, + ) -> Result + where + Self: 'a; +} diff --git a/src/protocol/iors/ood.rs b/src/protocol/iors/ood.rs new file mode 100644 index 0000000..cbe9ad8 --- /dev/null +++ b/src/protocol/iors/ood.rs @@ -0,0 +1,147 @@ +//! Out-of-domain sampling phase. +//! +//! Paired spec: `docs/paper-mods/mod1_oracle.tex`. This phase is a thin +//! composition of point queries on the committed oracle — see +//! [`Oracle::query_at_point`](crate::protocol::oracle::Oracle::query_at_point). +//! The verifier derives the same random points from the transcript. +//! +//! IOR ports +//! --------- +//! - input (prover): `{ oracle, s, log_n }` +//! - input (verifier): `{ s, log_n }` +//! - `reduced`: `{ samples_flat, answers }` — same on both sides (verifier +//! reads from the transcript) +//! - no `carry` — OOD's outputs all flow through `reduced` + +use ark_ff::{Field, PrimeField}; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; +use std::marker::PhantomData; + +use crate::count_ops; +use crate::error::{ProverError, VerifierError}; +use crate::protocol::oracle::Oracle; +use crate::protocol::iors::IOR; + +// ─── Inputs ─────────────────────────────────────────────────────────────── + +pub struct OodProverInput<'a, F: Field> { + pub oracle: &'a Oracle, + pub s: usize, + pub log_n: usize, +} + +pub struct OodVerifierInput { + pub s: usize, + pub log_n: usize, +} + +// ─── Output ports ───────────────────────────────────────────────────────── + +/// Public reduced claim — same on both sides. +pub struct OodReduced { + /// Flat challenge vector of length `s · log_n`. + pub samples_flat: Vec, + /// Answers `\hat f(ζ_j)` for each of the `s` chunked challenges. + pub answers: Vec, +} + +pub struct OodProverOutput { + pub reduced: OodReduced, + pub carry: (), +} + +pub struct OodVerifierOutput { + pub reduced: OodReduced, + pub carry: (), +} + +// ─── IOR ────────────────────────────────────────────────────────────────── + +/// OOD phase configuration. Stateless; the lifetime parameter exists only +/// to anchor `ProverInput<'a>` for the trait impl. +pub struct Ood<'a, F: Field> { + pub _phantom: PhantomData<&'a F>, +} + +impl<'a, F: Field> Ood<'a, F> { + pub fn new() -> Self { + Self { + _phantom: PhantomData, + } + } +} + +impl<'a, F: Field> Default for Ood<'a, F> { + fn default() -> Self { + Self::new() + } +} + +impl<'a, F> IOR for Ood<'a, F> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, +{ + const NAME: &'static str = "OOD"; + + type ProverInput<'b> + = OodProverInput<'b, F> + where + Self: 'b; + type ProverOutput = OodProverOutput; + type VerifierInput<'b> + = OodVerifierInput + where + Self: 'b; + type VerifierOutput = OodVerifierOutput; + + #[tracing::instrument(name = "ood", skip_all, fields(s = input.s, log_n = input.log_n))] + fn prove<'b>( + &self, + transcript: &mut ProverState, + input: Self::ProverInput<'b>, + ) -> Result + where + Self: 'b, + { + let samples_flat = transcript.verifier_messages_vec::(input.s * input.log_n); + count_ops!(OodPointQueries, input.s as u64); + let answers = samples_flat + .chunks(input.log_n) + .map(|zeta| input.oracle.query_at_point(zeta)) + .collect::>(); + transcript.prover_messages(&answers); + Ok(OodProverOutput { + reduced: OodReduced { + samples_flat, + answers, + }, + carry: (), + }) + } + + #[tracing::instrument( + name = "ood.verify", + skip_all, + fields(s = input.s, log_n = input.log_n) + )] + fn verify<'b, 'v>( + &self, + transcript: &mut VerifierState<'v>, + input: Self::VerifierInput<'b>, + ) -> Result + where + Self: 'b, + { + let samples_flat: Vec = (0..input.s * input.log_n) + .map(|_| transcript.verifier_message::()) + .collect(); + let answers: Vec = transcript.prover_messages_vec(input.s)?; + Ok(OodVerifierOutput { + reduced: OodReduced { + samples_flat, + answers, + }, + carry: (), + }) + } +} diff --git a/src/protocol/phases/oracle_handle.rs b/src/protocol/iors/oracle_handle.rs similarity index 100% rename from src/protocol/phases/oracle_handle.rs rename to src/protocol/iors/oracle_handle.rs diff --git a/src/protocol/iors/pesat.rs b/src/protocol/iors/pesat.rs new file mode 100644 index 0000000..31a240a --- /dev/null +++ b/src/protocol/iors/pesat.rs @@ -0,0 +1,189 @@ +//! PESAT Reduction IOR. +//! +//! Encodes fresh witnesses into codewords, commits via a multi-vector +//! Merkle tree (one root over all `l1` codewords), absorbs commitment + +//! code evaluations, and derives the τ zero-check challenges. +//! +//! IOR ports +//! --------- +//! - input (prover): `{ l1, log_m, witnesses }` +//! - input (verifier): `{ l1, log_m }` +//! - `reduced`: `{ mus, taus }` — same on both sides (verifier reads +//! from the transcript) +//! - `carry` (prover): `{ codewords, td_0 }` — feeds TwinConstraint / +//! Proximity +//! - `carry` (verifier): `{ rt_0 }` — feeds Proximity + +use ark_codes::traits::LinearCode; +use ark_ff::{Field, PrimeField}; +use ark_mt::MerkleHasher; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; +use std::marker::PhantomData; + +use crate::count_ops; +use crate::crypto::merkle::{encode_codewords, warp_scheme, WarpCommitted}; +use crate::error::{ProverError, VerifierError}; +use crate::protocol::iors::IOR; + +// ─── Inputs ─────────────────────────────────────────────────────────────── + +pub struct PesatProverInput<'a, F: Field> { + pub l1: usize, + pub log_m: usize, + pub witnesses: &'a [Vec], +} + +pub struct PesatVerifierInput { + pub l1: usize, + pub log_m: usize, +} + +// ─── Output ports ───────────────────────────────────────────────────────── + +/// Public reduced claim — same on both sides. +pub struct PesatReduced { + pub mus: Vec, + pub taus: Vec>, +} + +pub struct PesatProverCarry +where + F: Field, + H: MerkleHasher>, +{ + pub codewords: Vec>, + pub td_0: WarpCommitted, +} + +pub struct PesatVerifierCarry { + pub rt_0: H::Digest, +} + +pub struct PesatProverOutput +where + F: Field, + H: MerkleHasher>, +{ + pub reduced: PesatReduced, + pub carry: PesatProverCarry, +} + +pub struct PesatVerifierOutput { + pub reduced: PesatReduced, + pub carry: PesatVerifierCarry, +} + +// ─── IOR ────────────────────────────────────────────────────────────────── + +pub struct Pesat<'a, F, C, H> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + C: LinearCode, + H: MerkleHasher>, +{ + pub code: &'a C, + pub hasher: &'a H, + pub _phantom: PhantomData<(F, H)>, +} + +impl<'a, F, C, H> IOR for Pesat<'a, F, C, H> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + C: LinearCode, + H: MerkleHasher>, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, +{ + const NAME: &'static str = "PESAT"; + + type ProverInput<'b> + = PesatProverInput<'b, F> + where + Self: 'b; + type ProverOutput = PesatProverOutput; + type VerifierInput<'b> + = PesatVerifierInput + where + Self: 'b; + type VerifierOutput = PesatVerifierOutput; + + #[tracing::instrument( + name = "pesat", + skip_all, + fields(l1 = input.l1, log_m = input.log_m, n_witnesses = input.witnesses.len()) + )] + fn prove<'b>( + &self, + transcript: &mut ProverState, + input: Self::ProverInput<'b>, + ) -> Result + where + Self: 'b, + { + // a. encode witnesses + let codewords = { + let _s = tracing::info_span!("pesat.encode").entered(); + count_ops!(EncodeCalls, input.witnesses.len() as u64); + encode_codewords(self.code, input.witnesses) + }; + + // b. evaluation claims + let mus = codewords.iter().map(|f| f[0]).collect::>(); + + // c. commit to interleaved codewords (multi-vector commitment) + let td_0 = { + let _s = tracing::info_span!("pesat.merkle_commit").entered(); + count_ops!(MerkleTreeBuilds); + let scheme = warp_scheme(self.hasher.clone(), self.code.code_len()); + scheme.commit(&codewords) + }; + + // d. absorb commitment + claims; e/f. derive τ challenges. + let taus = { + let _s = tracing::info_span!("pesat.absorb_and_derive").entered(); + transcript.prover_message(td_0.root()); + transcript.prover_messages(&mus); + + (0..input.l1) + .map(|_| transcript.verifier_messages_vec::(input.log_m)) + .collect::>() + }; + + Ok(PesatProverOutput { + reduced: PesatReduced { + mus: mus.clone(), + taus, + }, + carry: PesatProverCarry { codewords, td_0 }, + }) + } + + #[tracing::instrument(name = "pesat.verify", skip_all, fields(l1 = input.l1, log_m = input.log_m))] + fn verify<'b, 'v>( + &self, + transcript: &mut VerifierState<'v>, + input: Self::VerifierInput<'b>, + ) -> Result + where + Self: 'b, + { + // commitment digest + let rt_0: H::Digest = transcript.prover_message()?; + + // mus (l1 evaluation claims) + let mus: Vec = transcript.prover_messages_vec(input.l1)?; + + // taus (l1 zero-check challenge vectors) + let taus: Vec> = (0..input.l1) + .map(|_| { + (0..input.log_m) + .map(|_| transcript.verifier_message::()) + .collect() + }) + .collect(); + + Ok(PesatVerifierOutput { + reduced: PesatReduced { mus, taus }, + carry: PesatVerifierCarry { rt_0 }, + }) + } +} diff --git a/src/protocol/iors/proximity.rs b/src/protocol/iors/proximity.rs new file mode 100644 index 0000000..ca6b1db --- /dev/null +++ b/src/protocol/iors/proximity.rs @@ -0,0 +1,313 @@ +//! Proximity / shift-query IOR. +//! +//! Index queries on the committed oracles. Opens both the fresh PESAT +//! commitment and each accumulated commitment at the query positions, +//! producing one multi-opening proof per commitment and a flat table of +//! the codeword values at those positions. +//! +//! IOR ports +//! --------- +//! - input (prover): `{ queries, td_0, acc_td, l2, t, n }` +//! - input (verifier): `{ queries, rt_0, l2_roots, auth_0, auth_j, +//! shift_query_answers, l2, t, n }` +//! - `proof` (prover): `ProximityProof { auth_0, auth_j, +//! shift_query_answers }` — collected into the global `WARPProof` +//! +//! Proximity is a check, not a reduction: there is no `reduced` or +//! `carry` port. The verifier-side input carries the raw BCS materials +//! (root digests, multi-opening proofs, answer tables); `verify` +//! internally builds the `MerkleIndexedOracle` handles and triggers +//! their (lazy, memoized) BCS validation. + +use ark_ff::Field; +use ark_mt::MerkleHasher; +use spongefish::{ProverState, VerifierState}; +use std::marker::PhantomData; + +use crate::count_ops; +use crate::crypto::merkle::{warp_scheme, WarpCommitted, WarpProof}; +use crate::error::{ProverError, VerifierError}; +use crate::protocol::iors::oracle_handle::{IndexedOracle, MerkleIndexedOracle}; +use crate::protocol::iors::IOR; +use crate::protocol::query::QueryIndices; + +// ─── Inputs ─────────────────────────────────────────────────────────────── + +pub struct ProximityProverInput<'a, F, H> +where + F: Field, + H: MerkleHasher>, +{ + pub queries: &'a QueryIndices, + pub td_0: &'a WarpCommitted, + pub acc_td: &'a [WarpCommitted], + pub l2: usize, + pub t: usize, + /// Codeword length (`code.code_len()`), needed to construct the + /// `WarpScheme` used for `open`. + pub n: usize, +} + +pub struct ProximityVerifierInput<'a, F, H> +where + F: Field, + H: MerkleHasher>, +{ + pub queries: &'a QueryIndices, + pub rt_0: &'a H::Digest, + pub l2_roots: &'a [H::Digest], + pub auth_0: &'a WarpProof, + pub auth_j: &'a [WarpProof], + /// Shift query answers: outer length = t (queries), + /// inner length = l2 + l1 (acc codewords ++ fresh codewords). + pub shift_query_answers: &'a [Vec], + pub l2: usize, + pub t: usize, + /// Codeword length (`code.code_len()`), needed to construct the + /// `WarpScheme` used for `check`. + pub n: usize, +} + +// ─── Output ports ───────────────────────────────────────────────────────── + +/// External proof material — collected into the global `WARPProof` by +/// the orchestrator. +pub struct ProximityProof +where + F: Field, + H: MerkleHasher, +{ + /// Single multi-opening proof for the fresh PESAT commitment. + pub auth_0: WarpProof, + /// One multi-opening proof per accumulated commitment. + pub auth_j: Vec>, + /// Shift query answers: per-query × per-codeword. + /// Outer length = t (queries). Inner length = (l2 acc + l1 fresh). + pub shift_query_answers: Vec>, +} + +pub struct ProximityProverOutput +where + F: Field, + H: MerkleHasher, +{ + pub reduced: (), + pub carry: (), + pub proof: ProximityProof, +} + +pub struct ProximityVerifierOutput { + pub reduced: (), + pub carry: (), +} + +// ─── IOR ────────────────────────────────────────────────────────────────── + +/// Proximity phase configuration. Holds the hasher value used by both +/// `open` (prover side) and `check` (verifier side). +pub struct Proximity<'a, F, H> +where + F: Field, + H: MerkleHasher>, +{ + pub hasher: &'a H, + pub _phantom: PhantomData, +} + +impl<'a, F, H> IOR for Proximity<'a, F, H> +where + F: Field, + H: MerkleHasher>, +{ + const NAME: &'static str = "Proximity"; + + type ProverInput<'b> + = ProximityProverInput<'b, F, H> + where + Self: 'b; + type ProverOutput = ProximityProverOutput; + type VerifierInput<'b> + = ProximityVerifierInput<'b, F, H> + where + Self: 'b; + type VerifierOutput = ProximityVerifierOutput; + + #[tracing::instrument( + name = "proximity", + skip_all, + fields( + n_queries = input.queries.leaf_positions.len(), + n_accumulators = input.acc_td.len(), + ) + )] + fn prove<'b>( + &self, + _transcript: &mut ProverState, + input: Self::ProverInput<'b>, + ) -> Result + where + Self: 'b, + { + let leaf_positions = &input.queries.leaf_positions; + + // ark-mt's `open()` requires strictly-sorted, unique indices. Query + // positions can repeat or arrive unsorted; deduplicate for the + // merkle opening, but keep `shift_query_answers` in original query + // order so downstream phases (batching) can index by query. + let mut sorted_unique = leaf_positions.clone(); + sorted_unique.sort_unstable(); + sorted_unique.dedup(); + + let scheme = warp_scheme(self.hasher.clone(), input.n); + + let auth_0 = { + let _s = tracing::info_span!("proximity.auth_0").entered(); + count_ops!(MerklePathsGenerated, sorted_unique.len() as u64); + scheme.open(input.td_0, &sorted_unique) + }; + + let auth_j: Vec> = { + let _s = tracing::info_span!("proximity.auth_j").entered(); + count_ops!( + MerklePathsGenerated, + (input.acc_td.len() * sorted_unique.len()) as u64 + ); + input + .acc_td + .iter() + .map(|td| scheme.open(td, &sorted_unique)) + .collect() + }; + + // Shift query answers: per query position, values across + // (acc_codewords ++ fresh_codewords). + let shift_query_answers = { + let _s = tracing::info_span!("proximity.shift_queries").entered(); + let total_codewords = input + .acc_td + .iter() + .map(|td| td.num_codewords()) + .sum::() + + input.td_0.num_codewords(); + let mut answers = vec![vec![F::default(); total_codewords]; leaf_positions.len()]; + for (qi, idx) in leaf_positions.iter().enumerate() { + let mut col = 0usize; + for td in input.acc_td.iter() { + for cw in td.codewords() { + answers[qi][col] = cw[*idx]; + col += 1; + } + } + for cw in input.td_0.codewords() { + answers[qi][col] = cw[*idx]; + col += 1; + } + } + answers + }; + + Ok(ProximityProverOutput { + reduced: (), + carry: (), + proof: ProximityProof { + auth_0, + auth_j, + shift_query_answers, + }, + }) + } + + #[tracing::instrument( + name = "proximity.verify", + skip_all, + fields(t = input.t, l2 = input.l2) + )] + fn verify<'b, 'v>( + &self, + _transcript: &mut VerifierState<'v>, + input: Self::VerifierInput<'b>, + ) -> Result + where + Self: 'b, + { + // ── Whole-proof arity checks (orchestrator used to own these) ── + (input.shift_query_answers.len() == input.t) + .then_some(()) + .ok_or(VerifierError::NumShiftQueries)?; + (input.auth_j.len() == input.l2) + .then_some(()) + .ok_or(VerifierError::NumL2Instances)?; + (input.l2_roots.len() == input.l2) + .then_some(()) + .ok_or(VerifierError::NumL2Instances)?; + + // ── Build IndexedOracle handles from raw BCS materials ──────── + // + // ark-mt's `check()` requires sorted, unique indices paired with + // the values at those indices. Query positions can repeat or + // arrive unsorted; we dedupe + sort here, then look up the + // corresponding row in `shift_query_answers` (which stays in + // original query order so downstream phases index by query). + let leaf_positions = &input.queries.leaf_positions; + let mut indexed: Vec<(usize, usize)> = leaf_positions + .iter() + .copied() + .enumerate() + .map(|(row, pos)| (pos, row)) + .collect(); + indexed.sort_by_key(|&(pos, _)| pos); + indexed.dedup_by_key(|&mut (pos, _)| pos); + let sorted_unique: Vec = indexed.iter().map(|&(p, _)| p).collect(); + let row_indices: Vec = indexed.iter().map(|&(_, r)| r).collect(); + + // Fresh handle: pull columns [l2..] (the l1 fresh codewords) + // from each authenticated row. + let fresh_values: Vec> = row_indices + .iter() + .map(|&r| input.shift_query_answers[r][input.l2..].to_vec()) + .collect(); + let fresh_handle = MerkleIndexedOracle::new( + warp_scheme::(self.hasher.clone(), input.n), + input.rt_0, + input.auth_0, + sorted_unique.clone(), + fresh_values, + ); + + // Acc handles: one per accumulated commitment, single-codeword. + let acc_handles: Vec> = (0..input.l2) + .map(|j| { + let acc_values: Vec> = row_indices + .iter() + .map(|&r| vec![input.shift_query_answers[r][j]]) + .collect(); + MerkleIndexedOracle::new( + warp_scheme::(self.hasher.clone(), input.n), + &input.l2_roots[j], + &input.auth_j[j], + sorted_unique.clone(), + acc_values, + ) + }) + .collect(); + + // ── Validate each handle (runs the BCS check internally) ────── + fresh_handle + .validate() + .then_some(()) + .ok_or(VerifierError::ShiftQuery)?; + count_ops!(MerklePathsVerified, leaf_positions.len() as u64); + for handle in acc_handles.iter() { + handle + .validate() + .then_some(()) + .ok_or(VerifierError::ShiftQuery)?; + count_ops!(MerklePathsVerified, leaf_positions.len() as u64); + } + + Ok(ProximityVerifierOutput { + reduced: (), + carry: (), + }) + } +} diff --git a/src/protocol/iors/sample_queries.rs b/src/protocol/iors/sample_queries.rs new file mode 100644 index 0000000..540c9dd --- /dev/null +++ b/src/protocol/iors/sample_queries.rs @@ -0,0 +1,128 @@ +//! Shift-query sampling phase. +//! +//! Between OOD and Batching, the verifier draws `t · log_n` random +//! bits and decodes them into `t` shift-query positions over +//! `{0, 1}^log_n`, plus their corresponding evaluation-point vectors. +//! +//! This is the simplest possible IOR — one round of pure verifier +//! randomness, no prover messages, no oracles. Modeled as an IOR for +//! uniformity with the rest of the choreography. +//! +//! IOR ports +//! --------- +//! - input (prover): `{ log_n, t }` +//! - input (verifier): `{ log_n, t }` +//! - `reduced`: `{ queries }` — same on both sides (verifier squeezes +//! the same bytes the prover did) + +use ark_ff::Field; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; +use std::marker::PhantomData; + +use crate::error::{ProverError, VerifierError}; +use crate::protocol::iors::IOR; +use crate::protocol::query::QueryIndices; + +// ─── Inputs ─────────────────────────────────────────────────────────────── + +pub struct SampleQueriesProverInput { + pub log_n: usize, + pub t: usize, +} + +pub struct SampleQueriesVerifierInput { + pub log_n: usize, + pub t: usize, +} + +// ─── Output ports ───────────────────────────────────────────────────────── + +/// Public reduced claim — same on both sides. +pub struct SampleQueriesReduced { + pub queries: QueryIndices, +} + +pub struct SampleQueriesProverOutput { + pub reduced: SampleQueriesReduced, + pub carry: (), +} + +pub struct SampleQueriesVerifierOutput { + pub reduced: SampleQueriesReduced, + pub carry: (), +} + +// ─── IOR ────────────────────────────────────────────────────────────────── + +/// Shift-query sampling phase configuration. Stateless. +pub struct SampleQueries<'a, F: Field> { + pub _phantom: PhantomData<&'a F>, +} + +impl<'a, F: Field> SampleQueries<'a, F> { + pub fn new() -> Self { + Self { + _phantom: PhantomData, + } + } +} + +impl<'a, F: Field> Default for SampleQueries<'a, F> { + fn default() -> Self { + Self::new() + } +} + +impl<'a, F> IOR for SampleQueries<'a, F> +where + F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, +{ + const NAME: &'static str = "SampleQueries"; + + type ProverInput<'b> + = SampleQueriesProverInput + where + Self: 'b; + type ProverOutput = SampleQueriesProverOutput; + type VerifierInput<'b> + = SampleQueriesVerifierInput + where + Self: 'b; + type VerifierOutput = SampleQueriesVerifierOutput; + + #[tracing::instrument(name = "sample_queries", skip_all, fields(t = input.t, log_n = input.log_n))] + fn prove<'b>( + &self, + transcript: &mut ProverState, + input: Self::ProverInput<'b>, + ) -> Result + where + Self: 'b, + { + let queries = QueryIndices::::sample(transcript, input.log_n, input.t); + Ok(SampleQueriesProverOutput { + reduced: SampleQueriesReduced { queries }, + carry: (), + }) + } + + #[tracing::instrument(name = "sample_queries.verify", skip_all, fields(t = input.t, log_n = input.log_n))] + fn verify<'b, 'v>( + &self, + transcript: &mut VerifierState<'v>, + input: Self::VerifierInput<'b>, + ) -> Result + where + Self: 'b, + { + let n_bytes = (input.t * input.log_n).div_ceil(8); + let bytes: Vec = (0..n_bytes) + .map(|_| transcript.verifier_message::<[u8; 1]>()[0]) + .collect(); + let queries = QueryIndices::from_squeezed_bytes(&bytes, input.log_n, input.t); + Ok(SampleQueriesVerifierOutput { + reduced: SampleQueriesReduced { queries }, + carry: (), + }) + } +} diff --git a/src/protocol/phases/twin_constraint.rs b/src/protocol/iors/twin_constraint.rs similarity index 62% rename from src/protocol/phases/twin_constraint.rs rename to src/protocol/iors/twin_constraint.rs index 2bad4a2..7e0a5fb 100644 --- a/src/protocol/phases/twin_constraint.rs +++ b/src/protocol/iors/twin_constraint.rs @@ -17,19 +17,23 @@ //! //! Each round's round polynomial has the form `h(X) = (f(X) + ω·p(X))·t(X)`. //! -//! IOR signature -//! ------------- -//! - `Statement` — accumulator instance + `l1_mus` + dimensions -//! - `Witness` — fresh witnesses + accumulator witness halves + R1CS -//! - `ProverInputs` — full codewords (from PESAT) + accumulated codewords -//! - `VerifierInputs` — `()` — the deferred oracle check (final\_claim ≟ eq(τ,γ)·(ν₀+ω·η)) -//! runs in the orchestrator after ν₀ and η arrive on the transcript -//! - `ReductionInputs` — `(ω, τ, γ, final_claim)` — both sides feed these into -//! `reduce_statement`, which computes ζ₀ / β_τ via `scale_and_sum` (single source of truth). -//! - `ReducedStatement` — sumcheck challenges γ + unchecked final_claim + ω, τ + new α (ζ₀) + new β_τ -//! - `ProofString` — `()` -//! - `ReducedWitness` — the new reduced oracle `f` + the reduced witness vector `z` -//! - `VerifierOutputs` — `()` (the new commitment is read from the transcript by the orchestrator) +//! IOR ports +//! --------- +//! - input (prover): `{ acc_instance, l1_mus, l1_taus, witnesses, +//! instances, acc_witness_w, fresh_codewords, acc_codewords, +//! log_l, log_m, log_n }` +//! - input (verifier): `{ parsed_acc, l1_mus, l1_taus, log_l, log_m, log_n }` +//! - `reduced`: `{ gamma, zeta_0, beta_tau, deferred }` — same on both +//! sides; ζ₀ and β_τ are computed from γ via [`compute_reduced`], +//! the single source of truth shared by `prove` and `verify`. +//! - `carry` (prover): `{ f, z }` — new reduced codeword oracle and the +//! reduced witness vector `z = (x, w)`, consumed by η evaluation. +//! - `carry` (verifier): none — the verifier has no oracle data to carry. +//! +//! The deferred oracle check `final_claim ≟ eq(τ, γ) · (ν₀ + ω·η)` cannot +//! complete inside `verify` because ν₀ and η arrive on the transcript +//! *after* the sumcheck rounds. The obligation is exposed as +//! [`DeferredOracleCheck`] inside `reduced` and discharged by Bridge. use ark_ff::{Field, PrimeField}; use ark_mt::MerkleHasher; @@ -49,7 +53,7 @@ use std::marker::PhantomData; use crate::count_ops; use crate::error::{ProverError, VerifierError}; use crate::protocol::oracle::Oracle; -use crate::protocol::phases::IOR; +use crate::protocol::iors::IOR; use crate::protocol::transcript::EffscVerifierTranscript; use crate::relations::r1cs::R1CSConstraints; use crate::types::AccumulatorInstance; @@ -182,29 +186,32 @@ impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { } } -// ─── IOR signature types ────────────────────────────────────────────────── +// ─── Inputs ─────────────────────────────────────────────────────────────── -pub struct TwinConstraintStatement { +pub struct TwinConstraintProverInput<'a, F: Field, H: MerkleHasher> { pub acc_instance: AccumulatorInstance, - pub l1_mus: Vec, - pub l1_taus: Vec>, + pub l1_mus: &'a [F], + pub l1_taus: &'a [Vec], + pub witnesses: &'a [Vec], + pub instances: &'a [Vec], + pub acc_witness_w: &'a [Vec], + pub fresh_codewords: &'a [Vec], + pub acc_codewords: &'a [Vec], pub log_l: usize, pub log_m: usize, pub log_n: usize, } -pub struct TwinConstraintWitness<'a, F: Field> { - pub acc_witness_w: &'a [Vec], - pub instances: &'a [Vec], - pub witnesses: &'a [Vec], +pub struct TwinConstraintVerifierInput<'a, F: Field, H: MerkleHasher> { + pub parsed_acc: AccumulatorInstance, + pub l1_mus: &'a [F], + pub l1_taus: &'a [Vec], + pub log_l: usize, + pub log_m: usize, + pub log_n: usize, } -pub struct TwinConstraintProverInputs<'a, F: Field> { - /// Fresh codewords emitted by PESAT. - pub fresh_codewords: &'a [Vec], - /// Accumulated codewords (from the accumulator witness). - pub acc_codewords: &'a [Vec], -} +// ─── Output ports ───────────────────────────────────────────────────────── /// A typed "you owe me a check" handle. /// @@ -213,7 +220,8 @@ pub struct TwinConstraintProverInputs<'a, F: Field> { /// completed inside `TwinConstraint::verify` because ν₀ and η arrive on the /// transcript *after* the sumcheck rounds. Rather than splitting /// TwinConstraint into two IORs (which cascades into other phases having -/// similar shapes), we expose the obligation as a typed value. +/// similar shapes), we expose the obligation as a typed value carried in +/// `reduced`. /// /// Discharge by calling [`Self::discharge`] with the missing inputs once the /// orchestrator has read them from the transcript. @@ -231,15 +239,16 @@ impl DeferredOracleCheck { /// Discharge the deferred check. /// /// Returns `Ok(())` iff `eq(τ, γ) · (ν₀ + ω·η) == claim`. `γ` arrives via - /// the parent `TwinConstraintReducedStatement`; `ν₀, η` come from the - /// transcript segment immediately following the TwinConstraint sumcheck. + /// the parent [`TwinConstraintReduced`]; `ν₀, η` come from the transcript + /// segment immediately following the TwinConstraint sumcheck. pub fn discharge(&self, gamma: &[F], nu_0: F, eta: F) -> Result<(), VerifierError> { let expected = eq_poly_non_binary(&self.tau, gamma) * (nu_0 + self.omega * eta); (expected == self.claim).then_some(()).ok_or(VerifierError::Target) } } -pub struct TwinConstraintReducedStatement { +/// Public reduced claim — same on both sides. +pub struct TwinConstraintReduced { /// Sumcheck challenge vector (LSB-indexed). pub gamma: Vec, /// New code-evaluation point (becomes the new accumulator's α). @@ -252,27 +261,70 @@ pub struct TwinConstraintReducedStatement { pub deferred: DeferredOracleCheck, } -/// Inputs to [`TwinConstraint::reduce_statement`]. Both prover and -/// verifier produce this struct from their respective machinery and feed -/// it into the shared reduction. Drift between the two sides is -/// structurally impossible because ζ₀ / β_τ are computed in exactly one -/// place — `reduce_statement` — using `scale_and_sum`. -pub struct TwinConstraintReductionInputs { - pub omega: F, - pub tau: Vec, - /// Sumcheck challenge vector (LSB-indexed). - pub gamma: Vec, - /// Sumcheck final value (the unchecked claim). - pub final_claim: F, -} - -pub struct TwinConstraintReducedWitness { +pub struct TwinConstraintProverCarry { /// New reduced codeword oracle. pub f: Oracle, /// Reduced witness vector `z = (x, w)` — consumed by η evaluation. pub z: Vec, } +pub struct TwinConstraintProverOutput { + pub reduced: TwinConstraintReduced, + pub carry: TwinConstraintProverCarry, +} + +pub struct TwinConstraintVerifierOutput { + pub reduced: TwinConstraintReduced, + pub carry: (), +} + +// ─── Private reduction helper ───────────────────────────────────────────── + +/// Single source of truth for ζ₀ / β_τ. Both prover and verifier land +/// here with `(ω, τ, γ, final_claim)` together with the public statement +/// pieces; the new accumulator state is computed identically on both +/// sides. +fn compute_reduced( + omega: F, + tau: Vec, + gamma: Vec, + final_claim: F, + acc_instance: &AccumulatorInstance, + l1_taus: &[Vec], + l1: usize, + log_l: usize, + log_n: usize, +) -> TwinConstraintReduced { + let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma); + + let alpha_vecs = concat_slices(&acc_instance.alpha, &vec![vec![F::zero(); log_n]; l1]); + let zeta_0 = scale_and_sum(&alpha_vecs, &gamma_eq_evals); + + // β τ-vectors: accumulated taus first (length l2), then PESAT taus + // (length l1). The τ component of the new β = Σ γ_eq(i) · β_i. + let beta_taus: Vec> = acc_instance + .beta + .0 + .iter() + .cloned() + .chain(l1_taus.iter().cloned()) + .collect(); + let beta_tau = scale_and_sum(&beta_taus, &gamma_eq_evals); + + TwinConstraintReduced { + gamma, + zeta_0, + beta_tau, + deferred: DeferredOracleCheck { + omega, + tau, + claim: final_claim, + }, + } +} + +// ─── IOR ────────────────────────────────────────────────────────────────── + /// TwinConstraint phase configuration. pub struct TwinConstraint<'a, F, H> where @@ -288,100 +340,40 @@ where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, H: MerkleHasher, { - type Statement = TwinConstraintStatement; - type Witness<'b> - = TwinConstraintWitness<'b, F> - where - Self: 'b; - type ProverInputs<'b> - = TwinConstraintProverInputs<'b, F> + const NAME: &'static str = "TwinConstraint"; + + type ProverInput<'b> + = TwinConstraintProverInput<'b, F, H> where Self: 'b; - type VerifierInputs<'b> - = () + type ProverOutput = TwinConstraintProverOutput; + type VerifierInput<'b> + = TwinConstraintVerifierInput<'b, F, H> where Self: 'b; - type ReductionInputs = TwinConstraintReductionInputs; - type ReducedStatement = TwinConstraintReducedStatement; - type ProofString = (); - type ReducedWitness = TwinConstraintReducedWitness; - type VerifierOutputs = (); - - /// Single source of truth for ζ₀ / β_τ. Both prover and verifier - /// land here with `(ω, τ, γ, final_claim)`; the new accumulator - /// state is computed identically on both sides. - fn reduce_statement( - &self, - statement: &Self::Statement, - inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement { - let log_l = statement.log_l; - let log_n = statement.log_n; - let l1 = statement.l1_mus.len(); - - let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &inputs.gamma); - - let alpha_vecs = concat_slices( - &statement.acc_instance.alpha, - &vec![vec![F::zero(); log_n]; l1], - ); - let zeta_0 = scale_and_sum(&alpha_vecs, &gamma_eq_evals); - - // β τ-vectors: accumulated taus first (length l2), then PESAT taus - // (length l1). The τ component of the new β = Σ γ_eq(i) · β_i. - let beta_taus: Vec> = statement - .acc_instance - .beta - .0 - .iter() - .cloned() - .chain(statement.l1_taus.iter().cloned()) - .collect(); - let beta_tau = scale_and_sum(&beta_taus, &gamma_eq_evals); - - TwinConstraintReducedStatement { - gamma: inputs.gamma.clone(), - zeta_0, - beta_tau, - deferred: DeferredOracleCheck { - omega: inputs.omega, - tau: inputs.tau.clone(), - claim: inputs.final_claim, - }, - } - } + type VerifierOutput = TwinConstraintVerifierOutput; #[tracing::instrument( name = "twin_constraint", skip_all, - fields(log_l = statement.log_l, log_m = statement.log_m, log_n = statement.log_n) + fields(log_l = input.log_l, log_m = input.log_m, log_n = input.log_n) )] - fn prove_inner<'b>( + fn prove<'b>( &self, - prover_state: &mut ProverState, - statement: &Self::Statement, - witness: &Self::Witness<'b>, - inputs: &Self::ProverInputs<'b>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > + transcript: &mut ProverState, + input: Self::ProverInput<'b>, + ) -> Result where - 'a: 'b, - H: 'b, + Self: 'b, { - let l1 = inputs.fresh_codewords.len(); - let log_l = statement.log_l; - let log_m = statement.log_m; - let log_n = statement.log_n; + let l1 = input.fresh_codewords.len(); + let log_l = input.log_l; + let log_m = input.log_m; + let log_n = input.log_n; // a. zero-check randomness - let omega: F = prover_state.verifier_message(); - let tau = prover_state.verifier_messages_vec::(log_l); + let omega: F = transcript.verifier_message(); + let tau = transcript.verifier_messages_vec::(log_l); // b. assemble sumcheck tables let tau_eq_evals = Ascending::new(log_l) @@ -389,35 +381,35 @@ where .collect::>(); let alpha_vecs = concat_slices( - &statement.acc_instance.alpha, + &input.acc_instance.alpha, &vec![vec![F::zero(); log_n]; l1], ); - let z_vecs: Vec> = statement + let z_vecs: Vec> = input .acc_instance .beta .1 .iter() - .zip(witness.acc_witness_w) - .chain(witness.instances.iter().zip(witness.witnesses)) + .zip(input.acc_witness_w) + .chain(input.instances.iter().zip(input.witnesses)) .map(|(x, w)| concat_slices(x, w)) .collect(); // β tables: accumulated β-τs first, then PESAT τs. - let beta_vecs: Vec> = statement + let beta_vecs: Vec> = input .acc_instance .beta .0 .iter() .cloned() - .chain(statement.l1_taus.iter().cloned()) + .chain(input.l1_taus.iter().cloned()) .collect(); let tablewise = vec![ - concat_slices(inputs.acc_codewords, inputs.fresh_codewords), // u - z_vecs, // z - alpha_vecs, // a - beta_vecs, // b + concat_slices(input.acc_codewords, input.fresh_codewords), // u + z_vecs, // z + alpha_vecs, // a + beta_vecs, // b ]; let pw = vec![tau_eq_evals]; // tau @@ -433,71 +425,76 @@ where let proof = { let _s = tracing::info_span!("twin_constraint.sumcheck").entered(); count_ops!(TwinConstraintRounds, log_l as u64); - sumcheck(&mut cc, log_l, prover_state, noop_hook) + sumcheck(&mut cc, log_l, transcript, noop_hook) }; debug_assert_eq!(proof.challenges.len(), log_l); // d. pull only the reduced *witness* halves out of CC. ζ₀ and β_τ - // are NOT pulled here — `reduce_statement` recomputes them from - // (statement, γ) via `scale_and_sum`, which is the single source - // of truth that both prover and verifier go through. - let reduced = cc.tablewise(); - debug_assert!(reduced.iter().all(|t| t.len() == 1)); - let f = reduced[0][0].clone(); - let z = reduced[1][0].clone(); - - Ok(( - TwinConstraintReductionInputs { - omega, - tau, - gamma: proof.challenges, - final_claim: proof.final_value, - }, - (), - TwinConstraintReducedWitness { + // are NOT pulled here — `compute_reduced` recomputes them from + // (input, γ) via `scale_and_sum`, which is the single source of + // truth that both prover and verifier go through. + let reduced_tables = cc.tablewise(); + debug_assert!(reduced_tables.iter().all(|t| t.len() == 1)); + let f = reduced_tables[0][0].clone(); + let z = reduced_tables[1][0].clone(); + + let reduced = compute_reduced::( + omega, + tau, + proof.challenges, + proof.final_value, + &input.acc_instance, + input.l1_taus, + l1, + log_l, + log_n, + ); + + Ok(TwinConstraintProverOutput { + reduced, + carry: TwinConstraintProverCarry { f: Oracle::from_evals(f), z, }, - )) + }) } #[tracing::instrument( name = "twin_constraint.verify", skip_all, - fields(log_l = statement.log_l, log_m = statement.log_m, log_n = statement.log_n) + fields(log_l = input.log_l, log_m = input.log_m, log_n = input.log_n) )] - fn verify_inner<'b, 'c>( + fn verify<'b, 'v>( &self, - verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement, - _inputs: &Self::VerifierInputs<'c>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + transcript: &mut VerifierState<'v>, + input: Self::VerifierInput<'b>, + ) -> Result where - 'a: 'c, - H: 'c, + Self: 'b, { - let log_l = statement.log_l; - let log_n = statement.log_n; - let l1 = statement.l1_mus.len(); + let log_l = input.log_l; + let log_m = input.log_m; + let log_n = input.log_n; + let l1 = input.l1_mus.len(); // Squeeze ω, τ matching the prover. - let omega: F = verifier_state.verifier_message(); + let omega: F = transcript.verifier_message(); let tau: Vec = (0..log_l) - .map(|_| verifier_state.verifier_message::()) + .map(|_| transcript.verifier_message::()) .collect(); // Compute σ₁ = Σ_i τ_eq(i) · (μ_i + ω · η_i). let tau_eq_evals = compute_hypercube_eq_evals(log_l, &tau); - let etas_l2_first = concat_slices(&statement.acc_instance.eta, &vec![F::zero(); l1]); + let etas_l2_first = concat_slices(&input.parsed_acc.eta, &vec![F::zero(); l1]); let sigma_1 = tau_eq_evals .into_iter() .zip( - statement - .acc_instance + input + .parsed_acc .mu .iter() .copied() - .chain(statement.l1_mus.iter().copied()) + .chain(input.l1_mus.iter().copied()) .zip(etas_l2_first), ) .fold(F::zero(), |acc, (eq_tau, (mu, eta))| { @@ -508,21 +505,25 @@ where // final_claim == eq(τ, γ) · (ν₀ + ω · η) // is left to the orchestrator because ν₀ and η arrive on the // transcript AFTER the sumcheck rounds. - let tc_degree = 1 + (log_n + 1).max(statement.log_m + 2); + let tc_degree = 1 + (log_n + 1).max(log_m + 2); let (gamma, final_claim) = { - let mut wrap = EffscVerifierTranscript(verifier_state); + let mut wrap = EffscVerifierTranscript(transcript); let res = sumcheck_verify(sigma_1, tc_degree, log_l, &mut wrap, |_, _| Ok(()))?; (res.challenges, res.final_claim) }; - Ok(( - TwinConstraintReductionInputs { - omega, - tau, - gamma, - final_claim, - }, - (), - )) + let reduced = compute_reduced::( + omega, + tau, + gamma, + final_claim, + &input.parsed_acc, + input.l1_taus, + l1, + log_l, + log_n, + ); + + Ok(TwinConstraintVerifierOutput { reduced, carry: () }) } } diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 55ed4d7..9d93709 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,4 +1,4 @@ pub mod oracle; -pub mod phases; +pub mod iors; pub mod query; pub mod transcript; diff --git a/src/protocol/phases/mod.rs b/src/protocol/phases/mod.rs deleted file mode 100644 index 2680e4e..0000000 --- a/src/protocol/phases/mod.rs +++ /dev/null @@ -1,184 +0,0 @@ -//! Warp IOR phases as first-class modules. -//! -//! Paired spec: `docs/paper-mods/mod1_oracle.tex` (composition rule) and the -//! forthcoming `docs/paper-mods/mod3_accumulator_state.tex`. -//! -//! Each submodule implements one Interactive Oracle Reduction from the Warp -//! construction. The [`IOR`] trait below names the paper-level components -//! and splits oracle types into prover-side / verifier-side halves so the -//! trait can serve both roles against the same struct. -//! -//! Implementor pattern: a phase is a struct holding setup parameters -//! (codes, hashers); implementors write [`IOR::prove_inner`], -//! [`IOR::verify_inner`], and [`IOR::reduce_statement`]. The trait provides -//! default `prove` / `verify` that mechanically chain -//! `*_inner -> reduce_statement` so the prover and verifier *cannot* drift -//! on how `ReducedStatement` is computed from transcript data. -//! -//! The top-level orchestrators in `src/lib.rs::WARP::prove` and `::verify` -//! thread state between phases by chaining `IOR::prove` / `IOR::verify` -//! calls — each phase's `ReducedStatement`, `ProofString`, and -//! `ReducedWitness` feed the next phase's `Statement` / `ProverInputs` / -//! the global proof object. - -// `prove_inner` returns the 3-tuple `(ReductionInputs, ProofString, -// ReducedWitness)`; clippy flags the resulting type as "complex" but -// the structure is exactly what the IOR formalism asks for. -#![allow(clippy::type_complexity)] - -pub mod batching; -pub mod bridge; -pub mod ood; -pub mod oracle_handle; -pub mod pesat; -pub mod proximity; -pub mod sample_queries; -pub mod twin_constraint; - -use spongefish::{ProverState, VerifierState}; - -use crate::error::{ProverError, VerifierError}; - -/// Interactive Oracle Reduction. -/// -/// Mirrors the IOR signature from `docs/paper-mods/mod1_oracle.tex` §4: -/// -/// ```text -/// (stmt, wit, oracles_in) --> (stmt', oracles_out) -/// ``` -/// -/// **Convention.** `Witness` and `ProverInputs` are conventionally -/// reference-holding wrappers (e.g. `PesatWitness<'a, F> { witnesses: -/// &'a [Vec] }`). The trait takes them by `&` so the caller never has -/// to clone bulk data on the way in. None of WARP / WHIR / STIR / FRI / -/// sumcheck-based protocols consume their witness at the IOR level. -/// -/// **Drift prevention.** Implementors write [`prove_inner`], -/// [`verify_inner`], and [`reduce_statement`]. The default `prove` / -/// `verify` chain them automatically; the `ReducedStatement` is computed -/// in exactly one place (`reduce_statement`), eliminating the bug class -/// where prover and verifier compute the same reduction via parallel -/// code paths that silently diverge. -pub trait IOR { - /// Pre-reduction claim. Visible to prover and verifier. - type Statement; - /// Prover-only data the prover reads. Lifetime-parameterized so a - /// pipeline composition can borrow inter-phase data into the witness - /// for one `prove` call without the borrow leaking into the phase - /// struct's lifetime — see `crate::protocol::composition`. - type Witness<'a> - where - Self: 'a; - /// Oracles flowing in from upstream IORs (prover view: full data). - /// Lifetime-parameterized for the same reason as `Witness`. - type ProverInputs<'a> - where - Self: 'a; - /// Oracles flowing in from upstream IORs (verifier view: commitments). - type VerifierInputs<'a> - where - Self: 'a; - /// Explicit inputs that determine [`Self::ReducedStatement`]. Both - /// prover-side and verifier-side machinery produce this struct, then - /// hand it to [`Self::reduce_statement`] for the (single) statement - /// derivation. - type ReductionInputs; - /// Post-reduction claim. Visible to prover and verifier. Computed - /// exclusively by [`Self::reduce_statement`]. - type ReducedStatement; - /// Out-of-band proof bytes the verifier always reads (auth paths, - /// shift-query answers, …). The orchestrator collects these into the - /// global proof object. - type ProofString; - /// Reduced-witness handoff to the next IOR — full prover-side data - /// (oracles, witness vectors, committed trees). In terminal-round - /// protocols (e.g. WHIR's final fold) this can also be revealed to - /// the verifier; the trait does not assume it is private. - type ReducedWitness; - /// Verifier-side outputs (commitments, parsed digests). Threaded into - /// downstream phase `VerifierInputs`. - type VerifierOutputs; - - /// THE single source of truth for `ReducedStatement`. - /// - /// Both prover and verifier call this with their respective - /// [`Self::ReductionInputs`] (assembled in `prove_inner` / - /// `verify_inner`). Drift between sides is structurally impossible - /// because both go through this function. - fn reduce_statement( - &self, - statement: &Self::Statement, - inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement; - - /// Implementor's prover-side body. Runs the protocol's prover - /// machinery and returns the inputs `reduce_statement` needs, plus - /// the proof string and reduced witness. - fn prove_inner<'a>( - &self, - prover_state: &mut ProverState, - statement: &Self::Statement, - witness: &Self::Witness<'a>, - inputs: &Self::ProverInputs<'a>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > - where - Self: 'a; - - /// Implementor's verifier-side body. Runs the protocol's verifier - /// machinery (sumcheck checks, transcript reads, soundness checks) - /// and returns the inputs `reduce_statement` needs. - fn verify_inner<'a, 'b>( - &self, - verifier_state: &mut VerifierState<'a>, - statement: &Self::Statement, - inputs: &Self::VerifierInputs<'b>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> - where - Self: 'b; - - /// Default impl. Implementors should not override. - fn prove<'a>( - &self, - prover_state: &mut ProverState, - statement: &Self::Statement, - witness: &Self::Witness<'a>, - inputs: &Self::ProverInputs<'a>, - ) -> Result< - ( - Self::ReducedStatement, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > - where - Self: 'a, - { - let (red_inputs, proof, red_wit) = - self.prove_inner(prover_state, statement, witness, inputs)?; - let reduced = self.reduce_statement(statement, &red_inputs); - Ok((reduced, proof, red_wit)) - } - - /// Default impl. Implementors should not override. - fn verify<'a, 'b>( - &self, - verifier_state: &mut VerifierState<'a>, - statement: &Self::Statement, - inputs: &Self::VerifierInputs<'b>, - ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> - where - Self: 'b, - { - let (red_inputs, vouts) = self.verify_inner(verifier_state, statement, inputs)?; - let reduced = self.reduce_statement(statement, &red_inputs); - Ok((reduced, vouts)) - } -} diff --git a/src/protocol/phases/ood.rs b/src/protocol/phases/ood.rs deleted file mode 100644 index a799357..0000000 --- a/src/protocol/phases/ood.rs +++ /dev/null @@ -1,167 +0,0 @@ -//! Out-of-domain sampling phase. -//! -//! Paired spec: `docs/paper-mods/mod1_oracle.tex`. This phase is a thin -//! composition of point queries on the committed oracle — see -//! [`Oracle::query_at_point`](crate::protocol::oracle::Oracle::query_at_point). -//! The verifier derives the same random points from the transcript. -//! -//! IOR signature -//! ------------- -//! - `Statement` — `(s, log_n)` -//! - `Witness` — `()` -//! - `ProverInputs` — `&Oracle` (the committed oracle, full data) -//! - `VerifierInputs` — `()` (the oracle check is deferred to the batching -//! sumcheck's final claim) -//! - `ReductionInputs` — `(samples_flat, answers)` — both sides arrive here -//! from the same transcript reads and feed into `reduce_statement`. -//! - `ReducedStatement` — `(samples_flat, answers)` — query points + their answers -//! - `ProofString` — `()` -//! - `ReducedWitness` — `()` -//! - `VerifierOutputs` — `()` - -use ark_ff::{Field, PrimeField}; -use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; -use std::marker::PhantomData; - -use crate::count_ops; -use crate::error::{ProverError, VerifierError}; -use crate::protocol::oracle::Oracle; -use crate::protocol::phases::IOR; - -pub struct OodStatement { - pub s: usize, - pub log_n: usize, -} - -pub struct OodProverInputs<'a, F: Field> { - pub oracle: &'a Oracle, -} - -pub struct OodReductionInputs { - pub samples_flat: Vec, - pub answers: Vec, -} - -pub struct OodReducedStatement { - /// Flat challenge vector of length `s · log_n`. - pub samples_flat: Vec, - /// Answers `\hat f(ζ_j)` for each of the `s` chunked challenges. - pub answers: Vec, -} - -/// OOD phase configuration. Stateless; the lifetime parameter exists only -/// to anchor `ProverInputs<'a>` for the trait impl. -pub struct Ood<'a, F: Field> { - pub _phantom: PhantomData<&'a F>, -} - -impl<'a, F: Field> Ood<'a, F> { - pub fn new() -> Self { - Self { - _phantom: PhantomData, - } - } -} - -impl<'a, F: Field> Default for Ood<'a, F> { - fn default() -> Self { - Self::new() - } -} - -impl<'a, F> IOR for Ood<'a, F> -where - F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, -{ - type Statement = OodStatement; - type Witness<'b> - = () - where - Self: 'b; - type ProverInputs<'b> - = OodProverInputs<'b, F> - where - Self: 'b; - type VerifierInputs<'b> - = () - where - Self: 'b; - type ReductionInputs = OodReductionInputs; - type ReducedStatement = OodReducedStatement; - type ProofString = (); - type ReducedWitness = (); - type VerifierOutputs = (); - - fn reduce_statement( - &self, - _statement: &Self::Statement, - inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement { - OodReducedStatement { - samples_flat: inputs.samples_flat.clone(), - answers: inputs.answers.clone(), - } - } - - #[tracing::instrument(name = "ood", skip_all, fields(s = statement.s, log_n = statement.log_n))] - fn prove_inner<'b>( - &self, - prover_state: &mut ProverState, - statement: &Self::Statement, - _witness: &Self::Witness<'b>, - inputs: &Self::ProverInputs<'b>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > - where - 'a: 'b, - { - let samples_flat = prover_state.verifier_messages_vec::(statement.s * statement.log_n); - count_ops!(OodPointQueries, statement.s as u64); - let answers = samples_flat - .chunks(statement.log_n) - .map(|zeta| inputs.oracle.query_at_point(zeta)) - .collect::>(); - prover_state.prover_messages(&answers); - Ok(( - OodReductionInputs { - samples_flat, - answers, - }, - (), - (), - )) - } - - #[tracing::instrument( - name = "ood.verify", - skip_all, - fields(s = statement.s, log_n = statement.log_n) - )] - fn verify_inner<'b, 'c>( - &self, - verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement, - _inputs: &Self::VerifierInputs<'c>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> - where - 'a: 'c, - { - let samples_flat: Vec = (0..statement.s * statement.log_n) - .map(|_| verifier_state.verifier_message::()) - .collect(); - let answers: Vec = verifier_state.prover_messages_vec(statement.s)?; - Ok(( - OodReductionInputs { - samples_flat, - answers, - }, - (), - )) - } -} diff --git a/src/protocol/phases/pesat.rs b/src/protocol/phases/pesat.rs deleted file mode 100644 index 8504832..0000000 --- a/src/protocol/phases/pesat.rs +++ /dev/null @@ -1,209 +0,0 @@ -//! PESAT Reduction phase. -//! -//! Implements Phase 2 of the Warp prover: encode fresh witnesses into -//! codewords, commit via a multi-vector Merkle tree (one root over all -//! l1 codewords), absorb commitment + code evaluations, and derive the -//! τ zero-check challenges. -//! -//! IOR signature -//! ------------- -//! - `Statement` — `(l1, log_m)` -//! - `Witness` — `&[Vec]` (fresh witnesses to encode) -//! - `ProverInputs` — `()` -//! - `VerifierInputs` — `()` -//! - `ReductionInputs` — `(mus, taus)` — both sides derive from transcript -//! - `ReducedStatement` — `(mus, taus)` — code-eval claims + zero-check randomness -//! - `ProofString` — `()` -//! - `ReducedWitness` — full codewords + multi-vector commit -//! - `VerifierOutputs` — Merkle root only - -use ark_codes::traits::LinearCode; -use ark_ff::{Field, PrimeField}; -use ark_mt::MerkleHasher; -use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; -use std::marker::PhantomData; - -use crate::count_ops; -use crate::crypto::merkle::{encode_codewords, warp_scheme, WarpCommitted}; -use crate::error::{ProverError, VerifierError}; -use crate::protocol::phases::IOR; - -pub struct PesatStatement { - pub l1: usize, - pub log_m: usize, -} - -pub struct PesatWitness<'a, F: Field> { - pub witnesses: &'a [Vec], -} - -pub struct PesatReductionInputs { - pub mus: Vec, - pub taus: Vec>, -} - -pub struct PesatReducedStatement { - pub mus: Vec, - pub taus: Vec>, -} - -pub struct PesatReducedWitness -where - F: Field, - H: MerkleHasher>, -{ - pub codewords: Vec>, - pub td_0: WarpCommitted, -} - -pub struct PesatVerifierOutputs { - pub rt_0: H::Digest, -} - -/// PESAT phase configuration. -pub struct Pesat<'a, F, C, H> -where - F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - C: LinearCode, - H: MerkleHasher>, -{ - pub code: &'a C, - pub hasher: &'a H, - pub _phantom: PhantomData<(F, H)>, -} - -impl<'a, F, C, H> IOR for Pesat<'a, F, C, H> -where - F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - C: LinearCode, - H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, -{ - type Statement = PesatStatement; - type Witness<'b> - = PesatWitness<'b, F> - where - Self: 'b; - type ProverInputs<'b> - = () - where - Self: 'b; - type VerifierInputs<'b> - = () - where - Self: 'b; - type ReductionInputs = PesatReductionInputs; - type ReducedStatement = PesatReducedStatement; - type ProofString = (); - type ReducedWitness = PesatReducedWitness; - type VerifierOutputs = PesatVerifierOutputs; - - fn reduce_statement( - &self, - _statement: &Self::Statement, - inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement { - PesatReducedStatement { - mus: inputs.mus.clone(), - taus: inputs.taus.clone(), - } - } - - #[tracing::instrument( - name = "pesat", - skip_all, - fields(l1 = statement.l1, log_m = statement.log_m, n_witnesses = witness.witnesses.len()) - )] - fn prove_inner<'b>( - &self, - prover_state: &mut ProverState, - statement: &Self::Statement, - witness: &Self::Witness<'b>, - _inputs: &Self::ProverInputs<'b>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > - where - 'a: 'b, - H: 'b, - { - // a. encode witnesses - let codewords = { - let _s = tracing::info_span!("pesat.encode").entered(); - count_ops!(EncodeCalls, witness.witnesses.len() as u64); - encode_codewords(self.code, witness.witnesses) - }; - - // b. evaluation claims - let mus = codewords.iter().map(|f| f[0]).collect::>(); - - // c. commit to interleaved codewords (multi-vector commitment) - let td_0 = { - let _s = tracing::info_span!("pesat.merkle_commit").entered(); - count_ops!(MerkleTreeBuilds); - let scheme = warp_scheme(self.hasher.clone(), self.code.code_len()); - scheme.commit(&codewords) - }; - - // d. absorb commitment + claims; e/f. derive τ challenges. - let taus = { - let _s = tracing::info_span!("pesat.absorb_and_derive").entered(); - prover_state.prover_message(td_0.root()); - prover_state.prover_messages(&mus); - - (0..statement.l1) - .map(|_| prover_state.verifier_messages_vec::(statement.log_m)) - .collect::>() - }; - - Ok(( - PesatReductionInputs { - mus: mus.clone(), - taus, - }, - (), - PesatReducedWitness { codewords, td_0 }, - )) - } - - #[tracing::instrument( - name = "pesat.verify", - skip_all, - fields(l1 = statement.l1, log_m = statement.log_m) - )] - fn verify_inner<'b, 'c>( - &self, - verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement, - _inputs: &Self::VerifierInputs<'c>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> - where - 'a: 'c, - H: 'c, - { - // commitment digest - let rt_0: H::Digest = verifier_state.prover_message()?; - - // mus (l1 evaluation claims) - let mus: Vec = verifier_state.prover_messages_vec(statement.l1)?; - - // taus (l1 zero-check challenge vectors) - let taus: Vec> = (0..statement.l1) - .map(|_| { - (0..statement.log_m) - .map(|_| verifier_state.verifier_message::()) - .collect() - }) - .collect(); - - Ok(( - PesatReductionInputs { mus, taus }, - PesatVerifierOutputs { rt_0 }, - )) - } -} diff --git a/src/protocol/phases/proximity.rs b/src/protocol/phases/proximity.rs deleted file mode 100644 index 5ce6eaf..0000000 --- a/src/protocol/phases/proximity.rs +++ /dev/null @@ -1,269 +0,0 @@ -//! Proximity / shift-query phase. -//! -//! Index queries on the committed oracles. Opens both the fresh PESAT -//! commitment and each accumulated commitment at the query positions, -//! producing one multi-opening proof per commitment and a flat table of -//! the codeword values at those positions. -//! -//! IOR signature -//! ------------- -//! - `Statement` — `(queries, l2, t, n)`. `n` is the codeword length; -//! the verifier needs it to construct the `WarpScheme` used for `check`. -//! - `Witness` — `()` -//! - `ProverInputs` — fresh `WarpCommitted` + l2 accumulated `WarpCommitted`s -//! - `VerifierInputs` — fresh root + l2 acc roots + opening proofs + answers -//! - `ReductionInputs` — `()` (no reduction) -//! - `ReducedStatement` — `()` (Proximity is a check, not a reduction) -//! - `ProofString` — opening proofs + shift_query_answers -//! - `ReducedWitness` — `()` -//! - `VerifierOutputs` — `()` - -use ark_ff::Field; -use ark_mt::MerkleHasher; -use spongefish::{ProverState, VerifierState}; -use std::marker::PhantomData; - -use crate::count_ops; -use crate::crypto::merkle::{warp_scheme, WarpCommitted, WarpProof}; -use crate::error::{ProverError, VerifierError}; -use crate::protocol::phases::oracle_handle::IndexedOracle; -use crate::protocol::phases::IOR; -use crate::protocol::query::QueryIndices; - -pub struct ProximityStatement { - pub queries: QueryIndices, - pub l2: usize, - pub t: usize, - /// Codeword length (`code.code_len()`), needed by both prover and - /// verifier to construct the `WarpScheme` used for open/check. - pub n: usize, -} - -pub struct ProximityProverInputs<'a, F, H> -where - F: Field, - H: MerkleHasher>, -{ - pub td_0: &'a WarpCommitted, - pub acc_td: &'a [WarpCommitted], -} - -/// Verifier-side inputs for Proximity. -/// -/// The phase verifier no longer sees roots / opening proofs / answer -/// tables directly; it sees [`IndexedOracle`] handles and triggers -/// their (lazy, memoized) BCS validation. Phases stay BCS-agnostic at -/// the type level — the orchestrator picks the concrete handle type -/// (today: [`oracle_handle::MerkleIndexedOracle`]). -/// -/// Generic over `O: IndexedOracle>` so dispatch is static; no -/// trait objects. -pub struct ProximityVerifierInputs<'a, F, O> -where - F: Field, - O: IndexedOracle>, -{ - /// Handle for the fresh PESAT multi-vector commitment (m = l1). - pub fresh: &'a O, - /// One handle per accumulated commitment (each with m = 1). - pub acc: &'a [O], - pub _f: PhantomData, -} - -pub struct ProximityProofString -where - F: Field, - H: MerkleHasher, -{ - /// Single multi-opening proof for the fresh PESAT commitment. - pub auth_0: WarpProof, - /// One multi-opening proof per accumulated commitment. - pub auth_j: Vec>, - /// Shift query answers: per-query × per-codeword. - /// Outer length = t (queries). Inner length = (l2 acc + l1 fresh). - pub shift_query_answers: Vec>, -} - -/// Proximity phase configuration. Holds the hasher value used by both -/// `open` (prover side) and `check` (verifier side). -pub struct Proximity<'a, F, H> -where - F: Field, - H: MerkleHasher>, -{ - pub hasher: &'a H, - pub _phantom: PhantomData, -} - -impl<'a, F, H> IOR for Proximity<'a, F, H> -where - F: Field, - H: MerkleHasher>, -{ - type Statement = ProximityStatement; - type Witness<'b> - = () - where - Self: 'b; - type ProverInputs<'b> - = ProximityProverInputs<'b, F, H> - where - Self: 'b; - type VerifierInputs<'b> - = ProximityVerifierInputs< - 'b, - F, - crate::protocol::phases::oracle_handle::MerkleIndexedOracle<'b, F, H>, - > - where - Self: 'b; - type ReductionInputs = (); - type ReducedStatement = (); - type ProofString = ProximityProofString; - type ReducedWitness = (); - type VerifierOutputs = (); - - fn reduce_statement( - &self, - _statement: &Self::Statement, - _inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement { - } - - #[tracing::instrument( - name = "proximity", - skip_all, - fields( - n_queries = statement.queries.leaf_positions.len(), - n_accumulators = inputs.acc_td.len(), - ) - )] - fn prove_inner<'b>( - &self, - _prover_state: &mut ProverState, - statement: &Self::Statement, - _witness: &Self::Witness<'b>, - inputs: &Self::ProverInputs<'b>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > - where - 'a: 'b, - H: 'b, - { - let leaf_positions = &statement.queries.leaf_positions; - - // ark-mt's `open()` requires strictly-sorted, unique indices. Query - // positions can repeat or arrive unsorted; deduplicate for the - // merkle opening, but keep `shift_query_answers` in original query - // order so downstream phases (batching) can index by query. - let mut sorted_unique = leaf_positions.clone(); - sorted_unique.sort_unstable(); - sorted_unique.dedup(); - - let scheme = warp_scheme(self.hasher.clone(), statement.n); - - let auth_0 = { - let _s = tracing::info_span!("proximity.auth_0").entered(); - count_ops!(MerklePathsGenerated, sorted_unique.len() as u64); - scheme.open(inputs.td_0, &sorted_unique) - }; - - let auth_j: Vec> = { - let _s = tracing::info_span!("proximity.auth_j").entered(); - count_ops!( - MerklePathsGenerated, - (inputs.acc_td.len() * sorted_unique.len()) as u64 - ); - inputs - .acc_td - .iter() - .map(|td| scheme.open(td, &sorted_unique)) - .collect() - }; - - // Shift query answers: per query position, values across - // (acc_codewords ++ fresh_codewords). - let shift_query_answers = { - let _s = tracing::info_span!("proximity.shift_queries").entered(); - let total_codewords = inputs - .acc_td - .iter() - .map(|td| td.num_codewords()) - .sum::() - + inputs.td_0.num_codewords(); - let mut answers = vec![vec![F::default(); total_codewords]; leaf_positions.len()]; - for (qi, idx) in leaf_positions.iter().enumerate() { - let mut col = 0usize; - for td in inputs.acc_td.iter() { - for cw in td.codewords() { - answers[qi][col] = cw[*idx]; - col += 1; - } - } - for cw in inputs.td_0.codewords() { - answers[qi][col] = cw[*idx]; - col += 1; - } - } - answers - }; - - Ok(( - (), - ProximityProofString { - auth_0, - auth_j, - shift_query_answers, - }, - (), - )) - } - - #[tracing::instrument( - name = "proximity.verify", - skip_all, - fields(t = statement.t, l2 = statement.l2) - )] - fn verify_inner<'b, 'c>( - &self, - _verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement, - inputs: &Self::VerifierInputs<'c>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> - where - 'a: 'c, - H: 'c, - { - // Arity / shape checks (orchestrator-side concerns the phase still - // owns: number of accumulator openings must match l2; number of - // shift queries is implicitly checked by the handle constructors). - (inputs.acc.len() == statement.l2).then_some(()).ok_or(VerifierError::NumL2Instances)?; - - // Validate each oracle handle. The handle is a trait-object - // partial function: validate() runs the (lazy, memoized) BCS - // check internally — this phase code is BCS-agnostic. - inputs - .fresh - .validate() - .then_some(()).ok_or(VerifierError::ShiftQuery)?; - count_ops!( - MerklePathsVerified, - statement.queries.leaf_positions.len() as u64 - ); - for handle in inputs.acc.iter() { - handle.validate().then_some(()).ok_or(VerifierError::ShiftQuery)?; - count_ops!( - MerklePathsVerified, - statement.queries.leaf_positions.len() as u64 - ); - } - - Ok(((), ())) - } -} diff --git a/src/protocol/phases/sample_queries.rs b/src/protocol/phases/sample_queries.rs deleted file mode 100644 index 29d04d5..0000000 --- a/src/protocol/phases/sample_queries.rs +++ /dev/null @@ -1,135 +0,0 @@ -//! Shift-query sampling phase. -//! -//! Between OOD and Batching, the verifier draws `t · log_n` random -//! bits and decodes them into `t` shift-query positions over -//! `{0, 1}^log_n`, plus their corresponding evaluation-point vectors. -//! -//! This is the simplest possible IOR — one round of pure verifier -//! randomness, no prover messages, no oracles. Modeled as an IOR for -//! uniformity with the rest of the choreography. -//! -//! IOR signature -//! ------------- -//! - `Statement` — `(log_n, t)` -//! - `Witness` — `()` -//! - `ProverInputs` — `()` -//! - `VerifierInputs` — `()` -//! - `ReductionInputs` — the sampled [`QueryIndices`] -//! - `ReducedStatement` — same — both sides receive the queries -//! - `ProofString` — `()` -//! - `ReducedWitness` — `()` (no asymmetric data: both sides hold the same queries) -//! - `VerifierOutputs` — `()` - -use ark_ff::Field; -use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; -use std::marker::PhantomData; - -use crate::error::{ProverError, VerifierError}; -use crate::protocol::phases::IOR; -use crate::protocol::query::QueryIndices; - -pub struct SampleQueriesStatement { - pub log_n: usize, - pub t: usize, -} - -pub struct SampleQueriesReductionInputs { - pub queries: QueryIndices, -} - -pub struct SampleQueriesReducedStatement { - pub queries: QueryIndices, -} - -/// Shift-query sampling phase configuration. Stateless. -pub struct SampleQueries<'a, F: Field> { - pub _phantom: PhantomData<&'a F>, -} - -impl<'a, F: Field> SampleQueries<'a, F> { - pub fn new() -> Self { - Self { - _phantom: PhantomData, - } - } -} - -impl<'a, F: Field> Default for SampleQueries<'a, F> { - fn default() -> Self { - Self::new() - } -} - -impl<'a, F> IOR for SampleQueries<'a, F> -where - F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, -{ - type Statement = SampleQueriesStatement; - type Witness<'b> - = () - where - Self: 'b; - type ProverInputs<'b> - = () - where - Self: 'b; - type VerifierInputs<'b> - = () - where - Self: 'b; - type ReductionInputs = SampleQueriesReductionInputs; - type ReducedStatement = SampleQueriesReducedStatement; - type ProofString = (); - type ReducedWitness = (); - type VerifierOutputs = (); - - fn reduce_statement( - &self, - _statement: &Self::Statement, - inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement { - SampleQueriesReducedStatement { - queries: inputs.queries.clone(), - } - } - - #[tracing::instrument(name = "sample_queries", skip_all, fields(t = statement.t, log_n = statement.log_n))] - fn prove_inner<'b>( - &self, - prover_state: &mut ProverState, - statement: &Self::Statement, - _witness: &Self::Witness<'b>, - _inputs: &Self::ProverInputs<'b>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > - where - Self: 'b, - { - let queries = QueryIndices::::sample(prover_state, statement.log_n, statement.t); - Ok((SampleQueriesReductionInputs { queries }, (), ())) - } - - #[tracing::instrument(name = "sample_queries.verify", skip_all)] - fn verify_inner<'b, 'c>( - &self, - verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement, - _inputs: &Self::VerifierInputs<'c>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> - where - Self: 'c, - { - let n_bytes = (statement.t * statement.log_n).div_ceil(8); - let bytes: Vec = (0..n_bytes) - .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) - .collect(); - let queries = QueryIndices::from_squeezed_bytes(&bytes, statement.log_n, statement.t); - Ok((SampleQueriesReductionInputs { queries }, ())) - } -} From 8b1f49f3b2938b83f1c7a37bca785318166649b7 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 12 May 2026 14:36:33 +0200 Subject: [PATCH 54/68] Revert "ior: collapse 9-type IOR trait to (Input, Output) per side; IOR is the star" This reverts commit 94ac227c0df2e21c01576e92476df0f2d8dd6b60. --- src/lib.rs | 420 ++++++++++-------- src/protocol/iors/mod.rs | 88 ---- src/protocol/iors/ood.rs | 147 ------ src/protocol/iors/pesat.rs | 189 -------- src/protocol/iors/proximity.rs | 313 ------------- src/protocol/iors/sample_queries.rs | 128 ------ src/protocol/mod.rs | 2 +- src/protocol/{iors => phases}/batching.rs | 212 +++++---- src/protocol/{iors => phases}/bridge.rs | 193 ++++---- src/protocol/phases/mod.rs | 184 ++++++++ src/protocol/phases/ood.rs | 167 +++++++ .../{iors => phases}/oracle_handle.rs | 0 src/protocol/phases/pesat.rs | 209 +++++++++ src/protocol/phases/proximity.rs | 269 +++++++++++ src/protocol/phases/sample_queries.rs | 135 ++++++ .../{iors => phases}/twin_constraint.rs | 369 ++++++++------- 16 files changed, 1594 insertions(+), 1431 deletions(-) delete mode 100644 src/protocol/iors/mod.rs delete mode 100644 src/protocol/iors/ood.rs delete mode 100644 src/protocol/iors/pesat.rs delete mode 100644 src/protocol/iors/proximity.rs delete mode 100644 src/protocol/iors/sample_queries.rs rename src/protocol/{iors => phases}/batching.rs (60%) rename src/protocol/{iors => phases}/bridge.rs (50%) create mode 100644 src/protocol/phases/mod.rs create mode 100644 src/protocol/phases/ood.rs rename src/protocol/{iors => phases}/oracle_handle.rs (100%) create mode 100644 src/protocol/phases/pesat.rs create mode 100644 src/protocol/phases/proximity.rs create mode 100644 src/protocol/phases/sample_queries.rs rename src/protocol/{iors => phases}/twin_constraint.rs (62%) diff --git a/src/lib.rs b/src/lib.rs index b7f2152..1670530 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,32 +47,29 @@ pub mod prelude { use crate::crypto::merkle::warp_scheme; use error::{DeciderError, ProverError, VerifierError}; -use protocol::iors::{ +use protocol::phases::{ batching::{ - Batching, BatchingProverCarry, BatchingProverInput, BatchingProverOutput, BatchingReduced, - BatchingVerifierInput, BatchingVerifierOutput, + Batching, BatchingProverInputs, BatchingReducedStatement, BatchingReducedWitness, + BatchingStatement, BatchingVerifierInputs, }, bridge::{ - Bridge, BridgeProverCarry, BridgeProverInput, BridgeProverOutput, BridgeReduced, - BridgeVerifierInput, BridgeVerifierOutput, + Bridge, BridgeProverInputs, BridgeReducedStatement, BridgeReducedWitness, BridgeStatement, + BridgeVerifierInputs, BridgeWitness, }, - ood::{Ood, OodProverInput, OodProverOutput, OodReduced, OodVerifierInput, OodVerifierOutput}, + ood::{Ood, OodProverInputs, OodReducedStatement, OodStatement}, + oracle_handle::MerkleIndexedOracle, pesat::{ - Pesat, PesatProverCarry, PesatProverInput, PesatProverOutput, PesatReduced, - PesatVerifierCarry, PesatVerifierInput, PesatVerifierOutput, + Pesat, PesatReducedStatement, PesatReducedWitness, PesatStatement, PesatVerifierOutputs, + PesatWitness, }, proximity::{ - Proximity, ProximityProof, ProximityProverInput, ProximityProverOutput, - ProximityVerifierInput, ProximityVerifierOutput, - }, - sample_queries::{ - SampleQueries, SampleQueriesProverInput, SampleQueriesProverOutput, SampleQueriesReduced, - SampleQueriesVerifierInput, SampleQueriesVerifierOutput, + Proximity, ProximityProofString, ProximityProverInputs, ProximityStatement, + ProximityVerifierInputs, }, + sample_queries::{SampleQueries, SampleQueriesReducedStatement, SampleQueriesStatement}, twin_constraint::{ - TwinConstraint, TwinConstraintProverCarry, TwinConstraintProverInput, - TwinConstraintProverOutput, TwinConstraintReduced, TwinConstraintVerifierInput, - TwinConstraintVerifierOutput, + TwinConstraint, TwinConstraintProverInputs, TwinConstraintReducedStatement, + TwinConstraintReducedWitness, TwinConstraintStatement, TwinConstraintWitness, }, IOR, }; @@ -195,150 +192,157 @@ where } = acc_witness; let acc_fs: Vec> = acc_tds.iter().map(|td| td.codewords()[0].clone()).collect(); - // IOR instances (stateless or borrowing &self.params for the &self lifetime). - let pesat_ior = Pesat:: { + // Phase structs (held by value, borrow setup data for &self lifetime). + let pesat_phase = Pesat:: { code: &self.params.code, hasher: &self.params.hasher, _phantom: PhantomData, }; - let twin_constraint_ior = TwinConstraint:: { + let tc_phase = TwinConstraint:: { r1cs: self.params.p.constraints(), _phantom: PhantomData, }; - let bridge_ior = Bridge::::new(); - let ood_ior = Ood::::new(); - let sample_queries_ior = SampleQueries::::new(); - let batching_ior = Batching::::new(); - let proximity_ior = Proximity:: { + let bridge_phase = Bridge::::new(); + let ood_phase = Ood::::new(); + let sample_queries_phase = SampleQueries::::new(); + let batching_phase = Batching::::new(); + let proximity_phase = Proximity:: { hasher: &self.params.hasher, _phantom: PhantomData, }; - // ── IOR 1: PESAT — encode witnesses, commit, derive τ ──────── - let PesatProverOutput { - reduced: PesatReduced { mus, taus }, - carry: PesatProverCarry { codewords, td_0 }, - } = pesat_ior.prove( - prover_state, - PesatProverInput { - l1, - log_m, - witnesses: &witnesses, - }, - )?; - - // ── IOR 2: TwinConstraint — fused-fold sumcheck ────────────── - let TwinConstraintProverOutput { - reduced: - TwinConstraintReduced { - gamma: _, - zeta_0, - beta_tau, - deferred: _, + // ── Phase 2: PESAT — encode witnesses, commit, derive τ ────── + let (PesatReducedStatement { mus, taus }, _, PesatReducedWitness { codewords, td_0 }) = + pesat_phase.prove( + prover_state, + &PesatStatement { l1, log_m }, + &PesatWitness { + witnesses: &witnesses, }, - carry: TwinConstraintProverCarry { f, z }, - } = twin_constraint_ior.prove( + &(), + )?; + + // ── Phase 3a: TwinConstraint — fused-fold sumcheck ─────────── + let ( + TwinConstraintReducedStatement { + gamma: _, + zeta_0, + beta_tau, + deferred: _, + }, + _, + TwinConstraintReducedWitness { f, z }, + ) = tc_phase.prove( prover_state, - TwinConstraintProverInput { + &TwinConstraintStatement { acc_instance, - l1_mus: &mus, - l1_taus: &taus, - witnesses: &witnesses, - instances: &instances, - acc_witness_w: &acc_ws, - fresh_codewords: &codewords, - acc_codewords: &acc_fs, + l1_mus: mus.clone(), + l1_taus: taus, log_l, log_m, log_n, }, + &TwinConstraintWitness { + acc_witness_w: &acc_ws, + instances: &instances, + witnesses: &witnesses, + }, + &TwinConstraintProverInputs { + fresh_codewords: &codewords, + acc_codewords: &acc_fs, + }, )?; - // ── IOR 3: Bridge — publish (td_new, η, ν₀); split z ───────── - let BridgeProverOutput { - reduced: - BridgeReduced { - eta, - nu_0, - td_new_root: _, - }, - carry: BridgeProverCarry { td_new, new_x, new_w }, - } = bridge_ior.prove( + // ── Phase 3b: Bridge — publish (td_new, η, ν₀); split z ────── + let ( + BridgeReducedStatement { + eta, + nu_0, + td_new_root: _, + }, + _, + BridgeReducedWitness { + td_new, + new_x, + new_w, + }, + ) = bridge_phase.prove( prover_state, - BridgeProverInput { - zeta_0: &zeta_0, - beta_tau: &beta_tau, - z: &z, - f: &f, + &BridgeStatement { + zeta_0: zeta_0.clone(), + beta_tau: beta_tau.clone(), + log_m, + n_minus_k: N - k, + }, + &BridgeWitness { z: &z, f: &f }, + &BridgeProverInputs { bundled_pesat: &self.params.p, hasher: &self.params.hasher, code_len: n, - log_m, - n_minus_k: N - k, + _f: PhantomData, }, )?; - // ── IOR 4: OOD — query reduced oracle out of domain ────────── - let OodProverOutput { - reduced: OodReduced { samples_flat, answers }, - carry: (), - } = ood_ior.prove( + // ── Phase 3c: OOD — out-of-domain queries ──────────────────── + let (OodReducedStatement { samples_flat, answers }, _, _) = ood_phase.prove( prover_state, - OodProverInput { - oracle: &f, + &OodStatement { s: self.params.config.s, log_n, }, + &(), + &OodProverInputs { oracle: &f }, )?; - // ── IOR 5: SampleQueries — sample t shift positions ────────── - let SampleQueriesProverOutput { - reduced: SampleQueriesReduced { queries }, - carry: (), - } = sample_queries_ior.prove( + // ── Phase 3c → 3d: SampleQueries — sample t shift positions ── + let (SampleQueriesReducedStatement { queries }, _, _) = sample_queries_phase.prove( prover_state, - SampleQueriesProverInput { + &SampleQueriesStatement { log_n, t: self.params.config.t, }, + &(), + &(), )?; - // ── IOR 6: Batching — DAG fan-in over (ζ₀, OOD, queries) ───── - let BatchingProverOutput { - reduced: BatchingReduced { alpha }, - carry: BatchingProverCarry { mu }, - } = batching_ior.prove( - prover_state, - BatchingProverInput { - zeta_0: &zeta_0, - samples_flat: &samples_flat, - query_eval_points: &queries.evaluation_points, - oracle: &f, - s: self.params.config.s, - t: self.params.config.t, - log_n, - }, - )?; - - // ── IOR 7: Proximity — open committed oracles at shift positions ── - let ProximityProverOutput { - reduced: (), - carry: (), - proof: ProximityProof { + // ── Phase 3d: Batching — DAG fan-in over (ζ₀, OOD, queries) ── + let (BatchingReducedStatement { alpha }, _, BatchingReducedWitness { mu }) = batching_phase + .prove( + prover_state, + &BatchingStatement::from_phase_outputs( + zeta_0.clone(), + &samples_flat, + &queries.evaluation_points, + self.params.config.s, + self.params.config.t, + log_n, + ), + &(), + &BatchingProverInputs { oracle: &f }, + )?; + + // ── Phase 3e: Proximity — shift-query openings ─────────────── + let ( + _, + ProximityProofString { auth_0, auth_j, shift_query_answers, }, - } = proximity_ior.prove( + _, + ) = proximity_phase.prove( prover_state, - ProximityProverInput { - queries: &queries, - td_0: &td_0, - acc_td: &acc_tds, + &ProximityStatement { + queries: queries.clone(), l2, t: self.params.config.t, n, }, + &(), + &ProximityProverInputs { + td_0: &td_0, + acc_td: &acc_tds, + }, )?; // ── Assemble new accumulator state and proof ───────────────── @@ -407,113 +411,164 @@ where // statement — Proximity needs them for the per-acc Merkle handles. let l2_roots = parsed_acc.rt.clone(); - // IOR instances. - let pesat_ior = Pesat:: { + // Phase structs (held by value, borrow setup data for &self lifetime). + let pesat_phase = Pesat:: { code: &self.params.code, hasher: &self.params.hasher, _phantom: PhantomData, }; - let twin_constraint_ior = TwinConstraint:: { + let tc_phase = TwinConstraint:: { r1cs: self.params.p.constraints(), _phantom: PhantomData, }; - let bridge_ior = Bridge::::new(); - let ood_ior = Ood::::new(); - let sample_queries_ior = SampleQueries::::new(); - let batching_ior = Batching::::new(); - let proximity_ior = Proximity:: { + let bridge_phase = Bridge::::new(); + let ood_phase = Ood::::new(); + let sample_queries_phase = SampleQueries::::new(); + let batching_phase = Batching::::new(); + let proximity_phase = Proximity:: { hasher: &self.params.hasher, _phantom: PhantomData, }; - // ── IOR 1: PESAT — read commitment and (μ, τ) ──────────────── - let PesatVerifierOutput { - reduced: PesatReduced { + // ── Phase 2: PESAT::verify ────────────────────────────────── + let ( + PesatReducedStatement { mus: l1_mus, taus: l1_taus, }, - carry: PesatVerifierCarry { rt_0 }, - } = pesat_ior.verify(verifier_state, PesatVerifierInput { l1, log_m })?; - - // ── IOR 2: TwinConstraint::verify ──────────────────────────── - let TwinConstraintVerifierOutput { - reduced: - TwinConstraintReduced { - gamma, - zeta_0, - beta_tau: _, - deferred, - }, - carry: (), - } = twin_constraint_ior.verify( + PesatVerifierOutputs { rt_0 }, + ) = pesat_phase.verify(verifier_state, &PesatStatement { l1, log_m }, &())?; + + // ── Phase 3a: TwinConstraint::verify ──────────────────────── + let ( + TwinConstraintReducedStatement { + gamma, + zeta_0, + beta_tau, + deferred, + }, + _, + ) = tc_phase.verify( verifier_state, - TwinConstraintVerifierInput { - parsed_acc, - l1_mus: &l1_mus, - l1_taus: &l1_taus, + &TwinConstraintStatement { + acc_instance: parsed_acc, + l1_mus: l1_mus.clone(), + l1_taus: l1_taus.clone(), log_l, log_m, log_n, }, + &(), )?; - // ── IOR 3: Bridge — read (td, η, ν₀); discharge TC's deferred ─ - let BridgeVerifierOutput { - reduced: BridgeReduced { + // ── Phase 3b: Bridge::verify — read (td, η, ν₀), discharge TC's deferred check ─ + let ( + BridgeReducedStatement { eta: _, nu_0, td_new_root: _, }, - carry: (), - } = bridge_ior.verify( + _, + ) = bridge_phase.verify( verifier_state, - BridgeVerifierInput { - gamma: &gamma, + &BridgeStatement { + zeta_0: zeta_0.clone(), + beta_tau, + log_m, + n_minus_k: N - k, + }, + &BridgeVerifierInputs { deferred: &deferred, + gamma: &gamma, }, )?; - // ── IOR 4: OOD::verify ────────────────────────────────────── - let OodVerifierOutput { - reduced: OodReduced { samples_flat, answers }, - carry: (), - } = ood_ior.verify( + // ── Phase 3c: OOD::verify ─────────────────────────────────── + let (OodReducedStatement { samples_flat, answers }, _) = ood_phase.verify( verifier_state, - OodVerifierInput { + &OodStatement { s: self.params.config.s, log_n, }, + &(), )?; - // ── IOR 5: SampleQueries::verify ──────────────────────────── - let SampleQueriesVerifierOutput { - reduced: SampleQueriesReduced { queries }, - carry: (), - } = sample_queries_ior.verify( + // ── Phase 3c → 3d: SampleQueries::verify ──────────────────── + let (SampleQueriesReducedStatement { queries }, _) = sample_queries_phase.verify( verifier_state, - SampleQueriesVerifierInput { + &SampleQueriesStatement { log_n, t: self.params.config.t, }, + &(), )?; - // ── IOR 7: Proximity::verify (arity + handle building lives here) ── - let ProximityVerifierOutput { reduced: (), carry: () } = proximity_ior.verify( + // ── Whole-proof arity checks (orchestrator-owned) ──────────── + (proof.shift_query_answers.len() == self.params.config.t) + .then_some(()) + .ok_or(VerifierError::NumShiftQueries)?; + (proof.auth_j.len() == l2) + .then_some(()) + .ok_or(VerifierError::NumL2Instances)?; + + // ── Build IndexedOracle handles for Proximity ─────────────── + let mut indexed: Vec<(usize, usize)> = queries + .leaf_positions + .iter() + .copied() + .enumerate() + .map(|(row, pos)| (pos, row)) + .collect(); + indexed.sort_by_key(|&(pos, _)| pos); + indexed.dedup_by_key(|&mut (pos, _)| pos); + let sorted_unique: Vec = indexed.iter().map(|&(p, _)| p).collect(); + let row_indices: Vec = indexed.iter().map(|&(_, r)| r).collect(); + + let fresh_values: Vec> = row_indices + .iter() + .map(|&r| proof.shift_query_answers[r][l2..].to_vec()) + .collect(); + let fresh_handle = MerkleIndexedOracle::new( + warp_scheme::(self.params.hasher.clone(), n), + &rt_0, + &proof.auth_0, + sorted_unique.clone(), + fresh_values, + ); + + let acc_handles: Vec> = (0..l2) + .map(|j| { + let acc_values: Vec> = row_indices + .iter() + .map(|&r| vec![proof.shift_query_answers[r][j]]) + .collect(); + MerkleIndexedOracle::new( + warp_scheme::(self.params.hasher.clone(), n), + &l2_roots[j], + &proof.auth_j[j], + sorted_unique.clone(), + acc_values, + ) + }) + .collect(); + + // ── Phase 3e: Proximity::verify ───────────────────────────── + proximity_phase.verify( verifier_state, - ProximityVerifierInput { - queries: &queries, - rt_0: &rt_0, - l2_roots: &l2_roots, - auth_0: &proof.auth_0, - auth_j: &proof.auth_j, - shift_query_answers: &proof.shift_query_answers, + &ProximityStatement { + queries: queries.clone(), l2, t: self.params.config.t, n, }, + &ProximityVerifierInputs { + fresh: &fresh_handle, + acc: &acc_handles, + _f: PhantomData, + }, )?; - // ── Recompute ν vector from shift answers (orchestrator-owned) ── + // ── Recompute ν vector from shift answers ────────────────── let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma); let mut nus = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); nus.push(nu_0); @@ -526,21 +581,20 @@ where nus.push(nu_st); } - // ── IOR 6: Batching::verify ───────────────────────────────── - let BatchingVerifierOutput { - reduced: BatchingReduced { alpha }, - carry: (), - } = batching_ior.verify( + // ── Phase 3d: Batching::verify ───────────────────────────── + let (BatchingReducedStatement { alpha }, _) = batching_phase.verify( verifier_state, - BatchingVerifierInput { - zeta_0: &zeta_0, - samples_flat: &samples_flat, - query_eval_points: &queries.evaluation_points, + &BatchingStatement::from_phase_outputs( + zeta_0.clone(), + &samples_flat, + &queries.evaluation_points, + self.params.config.s, + self.params.config.t, + log_n, + ), + &BatchingVerifierInputs { nus, acc_mu: acc_mu_first, - s: self.params.config.s, - t: self.params.config.t, - log_n, }, )?; diff --git a/src/protocol/iors/mod.rs b/src/protocol/iors/mod.rs deleted file mode 100644 index 6cb2f0f..0000000 --- a/src/protocol/iors/mod.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! Warp IORs as first-class modules. -//! -//! Each submodule implements one Interactive Oracle Reduction from the -//! WARP construction. Every IOR has the same shape (a single typed -//! `ProverInput` and `VerifierInput`, returning a single typed -//! `ProverOutput` and `VerifierOutput`), so the orchestrator in -//! [`crate::WARP::prove`] / [`crate::WARP::verify`] is a destructured -//! choreography of IOR calls. -//! -//! Output structure convention: each `ProverOutput` / `VerifierOutput` -//! is built from named *ports*: -//! -//! - `reduced` — public reduced claim, identical on prover and verifier -//! sides (same type). Both endpoints derive their `reduced` from the -//! transcript reads. -//! - `carry` — endpoint-only local state needed by *later* IORs (prover -//! carry holds full oracle data, witnesses, commitments; verifier -//! carry holds parsed digests). -//! - `proof` — external proof material that the orchestrator collects -//! into the global `WARPProof`. Optional; many IORs have `()` here. -//! -//! Downstream IORs reference upstream ports explicitly by name in their -//! own `ProverInput` / `VerifierInput`. The WARP protocol is a DAG over -//! IORs, not a linear chain. - -pub mod batching; -pub mod bridge; -pub mod ood; -pub mod oracle_handle; -pub mod pesat; -pub mod proximity; -pub mod sample_queries; -pub mod twin_constraint; - -use spongefish::{ProverState, VerifierState}; - -use crate::error::{ProverError, VerifierError}; - -/// Interactive Oracle Reduction. -/// -/// The single abstraction in WARP's protocol layer. Each IOR exposes -/// `prove(transcript, input) -> output` and a symmetric `verify`. Both -/// sides return a typed `ProverOutput` / `VerifierOutput` whose fields -/// are named ports (`reduced`, `carry`, optionally `proof`) — the -/// caller destructures by name at the call site. -/// -/// Drift between prover and verifier sides is structural: the `reduced` -/// field on both outputs has the same type. Each IOR's implementation -/// is responsible for computing the same reduced value on both sides -/// (typically via a shared private helper inside its module). -pub trait IOR { - /// Human-readable identifier — matches the WARP paper's phase label. - const NAME: &'static str; - - /// Prover-side input. Bundles the public claim, prover-private - /// witness, and any upstream-IOR oracle data the prover consumes. - type ProverInput<'a> - where - Self: 'a; - - /// Prover-side output: `reduced` + `carry` + optional `proof`. - type ProverOutput; - - /// Verifier-side input. Bundles the public claim and any - /// upstream-IOR digests/handles the verifier consumes. - type VerifierInput<'a> - where - Self: 'a; - - /// Verifier-side output: `reduced` + `carry` + optional `proof`. - type VerifierOutput; - - fn prove<'a>( - &self, - transcript: &mut ProverState, - input: Self::ProverInput<'a>, - ) -> Result - where - Self: 'a; - - fn verify<'a, 'v>( - &self, - transcript: &mut VerifierState<'v>, - input: Self::VerifierInput<'a>, - ) -> Result - where - Self: 'a; -} diff --git a/src/protocol/iors/ood.rs b/src/protocol/iors/ood.rs deleted file mode 100644 index cbe9ad8..0000000 --- a/src/protocol/iors/ood.rs +++ /dev/null @@ -1,147 +0,0 @@ -//! Out-of-domain sampling phase. -//! -//! Paired spec: `docs/paper-mods/mod1_oracle.tex`. This phase is a thin -//! composition of point queries on the committed oracle — see -//! [`Oracle::query_at_point`](crate::protocol::oracle::Oracle::query_at_point). -//! The verifier derives the same random points from the transcript. -//! -//! IOR ports -//! --------- -//! - input (prover): `{ oracle, s, log_n }` -//! - input (verifier): `{ s, log_n }` -//! - `reduced`: `{ samples_flat, answers }` — same on both sides (verifier -//! reads from the transcript) -//! - no `carry` — OOD's outputs all flow through `reduced` - -use ark_ff::{Field, PrimeField}; -use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; -use std::marker::PhantomData; - -use crate::count_ops; -use crate::error::{ProverError, VerifierError}; -use crate::protocol::oracle::Oracle; -use crate::protocol::iors::IOR; - -// ─── Inputs ─────────────────────────────────────────────────────────────── - -pub struct OodProverInput<'a, F: Field> { - pub oracle: &'a Oracle, - pub s: usize, - pub log_n: usize, -} - -pub struct OodVerifierInput { - pub s: usize, - pub log_n: usize, -} - -// ─── Output ports ───────────────────────────────────────────────────────── - -/// Public reduced claim — same on both sides. -pub struct OodReduced { - /// Flat challenge vector of length `s · log_n`. - pub samples_flat: Vec, - /// Answers `\hat f(ζ_j)` for each of the `s` chunked challenges. - pub answers: Vec, -} - -pub struct OodProverOutput { - pub reduced: OodReduced, - pub carry: (), -} - -pub struct OodVerifierOutput { - pub reduced: OodReduced, - pub carry: (), -} - -// ─── IOR ────────────────────────────────────────────────────────────────── - -/// OOD phase configuration. Stateless; the lifetime parameter exists only -/// to anchor `ProverInput<'a>` for the trait impl. -pub struct Ood<'a, F: Field> { - pub _phantom: PhantomData<&'a F>, -} - -impl<'a, F: Field> Ood<'a, F> { - pub fn new() -> Self { - Self { - _phantom: PhantomData, - } - } -} - -impl<'a, F: Field> Default for Ood<'a, F> { - fn default() -> Self { - Self::new() - } -} - -impl<'a, F> IOR for Ood<'a, F> -where - F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, -{ - const NAME: &'static str = "OOD"; - - type ProverInput<'b> - = OodProverInput<'b, F> - where - Self: 'b; - type ProverOutput = OodProverOutput; - type VerifierInput<'b> - = OodVerifierInput - where - Self: 'b; - type VerifierOutput = OodVerifierOutput; - - #[tracing::instrument(name = "ood", skip_all, fields(s = input.s, log_n = input.log_n))] - fn prove<'b>( - &self, - transcript: &mut ProverState, - input: Self::ProverInput<'b>, - ) -> Result - where - Self: 'b, - { - let samples_flat = transcript.verifier_messages_vec::(input.s * input.log_n); - count_ops!(OodPointQueries, input.s as u64); - let answers = samples_flat - .chunks(input.log_n) - .map(|zeta| input.oracle.query_at_point(zeta)) - .collect::>(); - transcript.prover_messages(&answers); - Ok(OodProverOutput { - reduced: OodReduced { - samples_flat, - answers, - }, - carry: (), - }) - } - - #[tracing::instrument( - name = "ood.verify", - skip_all, - fields(s = input.s, log_n = input.log_n) - )] - fn verify<'b, 'v>( - &self, - transcript: &mut VerifierState<'v>, - input: Self::VerifierInput<'b>, - ) -> Result - where - Self: 'b, - { - let samples_flat: Vec = (0..input.s * input.log_n) - .map(|_| transcript.verifier_message::()) - .collect(); - let answers: Vec = transcript.prover_messages_vec(input.s)?; - Ok(OodVerifierOutput { - reduced: OodReduced { - samples_flat, - answers, - }, - carry: (), - }) - } -} diff --git a/src/protocol/iors/pesat.rs b/src/protocol/iors/pesat.rs deleted file mode 100644 index 31a240a..0000000 --- a/src/protocol/iors/pesat.rs +++ /dev/null @@ -1,189 +0,0 @@ -//! PESAT Reduction IOR. -//! -//! Encodes fresh witnesses into codewords, commits via a multi-vector -//! Merkle tree (one root over all `l1` codewords), absorbs commitment + -//! code evaluations, and derives the τ zero-check challenges. -//! -//! IOR ports -//! --------- -//! - input (prover): `{ l1, log_m, witnesses }` -//! - input (verifier): `{ l1, log_m }` -//! - `reduced`: `{ mus, taus }` — same on both sides (verifier reads -//! from the transcript) -//! - `carry` (prover): `{ codewords, td_0 }` — feeds TwinConstraint / -//! Proximity -//! - `carry` (verifier): `{ rt_0 }` — feeds Proximity - -use ark_codes::traits::LinearCode; -use ark_ff::{Field, PrimeField}; -use ark_mt::MerkleHasher; -use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; -use std::marker::PhantomData; - -use crate::count_ops; -use crate::crypto::merkle::{encode_codewords, warp_scheme, WarpCommitted}; -use crate::error::{ProverError, VerifierError}; -use crate::protocol::iors::IOR; - -// ─── Inputs ─────────────────────────────────────────────────────────────── - -pub struct PesatProverInput<'a, F: Field> { - pub l1: usize, - pub log_m: usize, - pub witnesses: &'a [Vec], -} - -pub struct PesatVerifierInput { - pub l1: usize, - pub log_m: usize, -} - -// ─── Output ports ───────────────────────────────────────────────────────── - -/// Public reduced claim — same on both sides. -pub struct PesatReduced { - pub mus: Vec, - pub taus: Vec>, -} - -pub struct PesatProverCarry -where - F: Field, - H: MerkleHasher>, -{ - pub codewords: Vec>, - pub td_0: WarpCommitted, -} - -pub struct PesatVerifierCarry { - pub rt_0: H::Digest, -} - -pub struct PesatProverOutput -where - F: Field, - H: MerkleHasher>, -{ - pub reduced: PesatReduced, - pub carry: PesatProverCarry, -} - -pub struct PesatVerifierOutput { - pub reduced: PesatReduced, - pub carry: PesatVerifierCarry, -} - -// ─── IOR ────────────────────────────────────────────────────────────────── - -pub struct Pesat<'a, F, C, H> -where - F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - C: LinearCode, - H: MerkleHasher>, -{ - pub code: &'a C, - pub hasher: &'a H, - pub _phantom: PhantomData<(F, H)>, -} - -impl<'a, F, C, H> IOR for Pesat<'a, F, C, H> -where - F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - C: LinearCode, - H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, -{ - const NAME: &'static str = "PESAT"; - - type ProverInput<'b> - = PesatProverInput<'b, F> - where - Self: 'b; - type ProverOutput = PesatProverOutput; - type VerifierInput<'b> - = PesatVerifierInput - where - Self: 'b; - type VerifierOutput = PesatVerifierOutput; - - #[tracing::instrument( - name = "pesat", - skip_all, - fields(l1 = input.l1, log_m = input.log_m, n_witnesses = input.witnesses.len()) - )] - fn prove<'b>( - &self, - transcript: &mut ProverState, - input: Self::ProverInput<'b>, - ) -> Result - where - Self: 'b, - { - // a. encode witnesses - let codewords = { - let _s = tracing::info_span!("pesat.encode").entered(); - count_ops!(EncodeCalls, input.witnesses.len() as u64); - encode_codewords(self.code, input.witnesses) - }; - - // b. evaluation claims - let mus = codewords.iter().map(|f| f[0]).collect::>(); - - // c. commit to interleaved codewords (multi-vector commitment) - let td_0 = { - let _s = tracing::info_span!("pesat.merkle_commit").entered(); - count_ops!(MerkleTreeBuilds); - let scheme = warp_scheme(self.hasher.clone(), self.code.code_len()); - scheme.commit(&codewords) - }; - - // d. absorb commitment + claims; e/f. derive τ challenges. - let taus = { - let _s = tracing::info_span!("pesat.absorb_and_derive").entered(); - transcript.prover_message(td_0.root()); - transcript.prover_messages(&mus); - - (0..input.l1) - .map(|_| transcript.verifier_messages_vec::(input.log_m)) - .collect::>() - }; - - Ok(PesatProverOutput { - reduced: PesatReduced { - mus: mus.clone(), - taus, - }, - carry: PesatProverCarry { codewords, td_0 }, - }) - } - - #[tracing::instrument(name = "pesat.verify", skip_all, fields(l1 = input.l1, log_m = input.log_m))] - fn verify<'b, 'v>( - &self, - transcript: &mut VerifierState<'v>, - input: Self::VerifierInput<'b>, - ) -> Result - where - Self: 'b, - { - // commitment digest - let rt_0: H::Digest = transcript.prover_message()?; - - // mus (l1 evaluation claims) - let mus: Vec = transcript.prover_messages_vec(input.l1)?; - - // taus (l1 zero-check challenge vectors) - let taus: Vec> = (0..input.l1) - .map(|_| { - (0..input.log_m) - .map(|_| transcript.verifier_message::()) - .collect() - }) - .collect(); - - Ok(PesatVerifierOutput { - reduced: PesatReduced { mus, taus }, - carry: PesatVerifierCarry { rt_0 }, - }) - } -} diff --git a/src/protocol/iors/proximity.rs b/src/protocol/iors/proximity.rs deleted file mode 100644 index ca6b1db..0000000 --- a/src/protocol/iors/proximity.rs +++ /dev/null @@ -1,313 +0,0 @@ -//! Proximity / shift-query IOR. -//! -//! Index queries on the committed oracles. Opens both the fresh PESAT -//! commitment and each accumulated commitment at the query positions, -//! producing one multi-opening proof per commitment and a flat table of -//! the codeword values at those positions. -//! -//! IOR ports -//! --------- -//! - input (prover): `{ queries, td_0, acc_td, l2, t, n }` -//! - input (verifier): `{ queries, rt_0, l2_roots, auth_0, auth_j, -//! shift_query_answers, l2, t, n }` -//! - `proof` (prover): `ProximityProof { auth_0, auth_j, -//! shift_query_answers }` — collected into the global `WARPProof` -//! -//! Proximity is a check, not a reduction: there is no `reduced` or -//! `carry` port. The verifier-side input carries the raw BCS materials -//! (root digests, multi-opening proofs, answer tables); `verify` -//! internally builds the `MerkleIndexedOracle` handles and triggers -//! their (lazy, memoized) BCS validation. - -use ark_ff::Field; -use ark_mt::MerkleHasher; -use spongefish::{ProverState, VerifierState}; -use std::marker::PhantomData; - -use crate::count_ops; -use crate::crypto::merkle::{warp_scheme, WarpCommitted, WarpProof}; -use crate::error::{ProverError, VerifierError}; -use crate::protocol::iors::oracle_handle::{IndexedOracle, MerkleIndexedOracle}; -use crate::protocol::iors::IOR; -use crate::protocol::query::QueryIndices; - -// ─── Inputs ─────────────────────────────────────────────────────────────── - -pub struct ProximityProverInput<'a, F, H> -where - F: Field, - H: MerkleHasher>, -{ - pub queries: &'a QueryIndices, - pub td_0: &'a WarpCommitted, - pub acc_td: &'a [WarpCommitted], - pub l2: usize, - pub t: usize, - /// Codeword length (`code.code_len()`), needed to construct the - /// `WarpScheme` used for `open`. - pub n: usize, -} - -pub struct ProximityVerifierInput<'a, F, H> -where - F: Field, - H: MerkleHasher>, -{ - pub queries: &'a QueryIndices, - pub rt_0: &'a H::Digest, - pub l2_roots: &'a [H::Digest], - pub auth_0: &'a WarpProof, - pub auth_j: &'a [WarpProof], - /// Shift query answers: outer length = t (queries), - /// inner length = l2 + l1 (acc codewords ++ fresh codewords). - pub shift_query_answers: &'a [Vec], - pub l2: usize, - pub t: usize, - /// Codeword length (`code.code_len()`), needed to construct the - /// `WarpScheme` used for `check`. - pub n: usize, -} - -// ─── Output ports ───────────────────────────────────────────────────────── - -/// External proof material — collected into the global `WARPProof` by -/// the orchestrator. -pub struct ProximityProof -where - F: Field, - H: MerkleHasher, -{ - /// Single multi-opening proof for the fresh PESAT commitment. - pub auth_0: WarpProof, - /// One multi-opening proof per accumulated commitment. - pub auth_j: Vec>, - /// Shift query answers: per-query × per-codeword. - /// Outer length = t (queries). Inner length = (l2 acc + l1 fresh). - pub shift_query_answers: Vec>, -} - -pub struct ProximityProverOutput -where - F: Field, - H: MerkleHasher, -{ - pub reduced: (), - pub carry: (), - pub proof: ProximityProof, -} - -pub struct ProximityVerifierOutput { - pub reduced: (), - pub carry: (), -} - -// ─── IOR ────────────────────────────────────────────────────────────────── - -/// Proximity phase configuration. Holds the hasher value used by both -/// `open` (prover side) and `check` (verifier side). -pub struct Proximity<'a, F, H> -where - F: Field, - H: MerkleHasher>, -{ - pub hasher: &'a H, - pub _phantom: PhantomData, -} - -impl<'a, F, H> IOR for Proximity<'a, F, H> -where - F: Field, - H: MerkleHasher>, -{ - const NAME: &'static str = "Proximity"; - - type ProverInput<'b> - = ProximityProverInput<'b, F, H> - where - Self: 'b; - type ProverOutput = ProximityProverOutput; - type VerifierInput<'b> - = ProximityVerifierInput<'b, F, H> - where - Self: 'b; - type VerifierOutput = ProximityVerifierOutput; - - #[tracing::instrument( - name = "proximity", - skip_all, - fields( - n_queries = input.queries.leaf_positions.len(), - n_accumulators = input.acc_td.len(), - ) - )] - fn prove<'b>( - &self, - _transcript: &mut ProverState, - input: Self::ProverInput<'b>, - ) -> Result - where - Self: 'b, - { - let leaf_positions = &input.queries.leaf_positions; - - // ark-mt's `open()` requires strictly-sorted, unique indices. Query - // positions can repeat or arrive unsorted; deduplicate for the - // merkle opening, but keep `shift_query_answers` in original query - // order so downstream phases (batching) can index by query. - let mut sorted_unique = leaf_positions.clone(); - sorted_unique.sort_unstable(); - sorted_unique.dedup(); - - let scheme = warp_scheme(self.hasher.clone(), input.n); - - let auth_0 = { - let _s = tracing::info_span!("proximity.auth_0").entered(); - count_ops!(MerklePathsGenerated, sorted_unique.len() as u64); - scheme.open(input.td_0, &sorted_unique) - }; - - let auth_j: Vec> = { - let _s = tracing::info_span!("proximity.auth_j").entered(); - count_ops!( - MerklePathsGenerated, - (input.acc_td.len() * sorted_unique.len()) as u64 - ); - input - .acc_td - .iter() - .map(|td| scheme.open(td, &sorted_unique)) - .collect() - }; - - // Shift query answers: per query position, values across - // (acc_codewords ++ fresh_codewords). - let shift_query_answers = { - let _s = tracing::info_span!("proximity.shift_queries").entered(); - let total_codewords = input - .acc_td - .iter() - .map(|td| td.num_codewords()) - .sum::() - + input.td_0.num_codewords(); - let mut answers = vec![vec![F::default(); total_codewords]; leaf_positions.len()]; - for (qi, idx) in leaf_positions.iter().enumerate() { - let mut col = 0usize; - for td in input.acc_td.iter() { - for cw in td.codewords() { - answers[qi][col] = cw[*idx]; - col += 1; - } - } - for cw in input.td_0.codewords() { - answers[qi][col] = cw[*idx]; - col += 1; - } - } - answers - }; - - Ok(ProximityProverOutput { - reduced: (), - carry: (), - proof: ProximityProof { - auth_0, - auth_j, - shift_query_answers, - }, - }) - } - - #[tracing::instrument( - name = "proximity.verify", - skip_all, - fields(t = input.t, l2 = input.l2) - )] - fn verify<'b, 'v>( - &self, - _transcript: &mut VerifierState<'v>, - input: Self::VerifierInput<'b>, - ) -> Result - where - Self: 'b, - { - // ── Whole-proof arity checks (orchestrator used to own these) ── - (input.shift_query_answers.len() == input.t) - .then_some(()) - .ok_or(VerifierError::NumShiftQueries)?; - (input.auth_j.len() == input.l2) - .then_some(()) - .ok_or(VerifierError::NumL2Instances)?; - (input.l2_roots.len() == input.l2) - .then_some(()) - .ok_or(VerifierError::NumL2Instances)?; - - // ── Build IndexedOracle handles from raw BCS materials ──────── - // - // ark-mt's `check()` requires sorted, unique indices paired with - // the values at those indices. Query positions can repeat or - // arrive unsorted; we dedupe + sort here, then look up the - // corresponding row in `shift_query_answers` (which stays in - // original query order so downstream phases index by query). - let leaf_positions = &input.queries.leaf_positions; - let mut indexed: Vec<(usize, usize)> = leaf_positions - .iter() - .copied() - .enumerate() - .map(|(row, pos)| (pos, row)) - .collect(); - indexed.sort_by_key(|&(pos, _)| pos); - indexed.dedup_by_key(|&mut (pos, _)| pos); - let sorted_unique: Vec = indexed.iter().map(|&(p, _)| p).collect(); - let row_indices: Vec = indexed.iter().map(|&(_, r)| r).collect(); - - // Fresh handle: pull columns [l2..] (the l1 fresh codewords) - // from each authenticated row. - let fresh_values: Vec> = row_indices - .iter() - .map(|&r| input.shift_query_answers[r][input.l2..].to_vec()) - .collect(); - let fresh_handle = MerkleIndexedOracle::new( - warp_scheme::(self.hasher.clone(), input.n), - input.rt_0, - input.auth_0, - sorted_unique.clone(), - fresh_values, - ); - - // Acc handles: one per accumulated commitment, single-codeword. - let acc_handles: Vec> = (0..input.l2) - .map(|j| { - let acc_values: Vec> = row_indices - .iter() - .map(|&r| vec![input.shift_query_answers[r][j]]) - .collect(); - MerkleIndexedOracle::new( - warp_scheme::(self.hasher.clone(), input.n), - &input.l2_roots[j], - &input.auth_j[j], - sorted_unique.clone(), - acc_values, - ) - }) - .collect(); - - // ── Validate each handle (runs the BCS check internally) ────── - fresh_handle - .validate() - .then_some(()) - .ok_or(VerifierError::ShiftQuery)?; - count_ops!(MerklePathsVerified, leaf_positions.len() as u64); - for handle in acc_handles.iter() { - handle - .validate() - .then_some(()) - .ok_or(VerifierError::ShiftQuery)?; - count_ops!(MerklePathsVerified, leaf_positions.len() as u64); - } - - Ok(ProximityVerifierOutput { - reduced: (), - carry: (), - }) - } -} diff --git a/src/protocol/iors/sample_queries.rs b/src/protocol/iors/sample_queries.rs deleted file mode 100644 index 540c9dd..0000000 --- a/src/protocol/iors/sample_queries.rs +++ /dev/null @@ -1,128 +0,0 @@ -//! Shift-query sampling phase. -//! -//! Between OOD and Batching, the verifier draws `t · log_n` random -//! bits and decodes them into `t` shift-query positions over -//! `{0, 1}^log_n`, plus their corresponding evaluation-point vectors. -//! -//! This is the simplest possible IOR — one round of pure verifier -//! randomness, no prover messages, no oracles. Modeled as an IOR for -//! uniformity with the rest of the choreography. -//! -//! IOR ports -//! --------- -//! - input (prover): `{ log_n, t }` -//! - input (verifier): `{ log_n, t }` -//! - `reduced`: `{ queries }` — same on both sides (verifier squeezes -//! the same bytes the prover did) - -use ark_ff::Field; -use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; -use std::marker::PhantomData; - -use crate::error::{ProverError, VerifierError}; -use crate::protocol::iors::IOR; -use crate::protocol::query::QueryIndices; - -// ─── Inputs ─────────────────────────────────────────────────────────────── - -pub struct SampleQueriesProverInput { - pub log_n: usize, - pub t: usize, -} - -pub struct SampleQueriesVerifierInput { - pub log_n: usize, - pub t: usize, -} - -// ─── Output ports ───────────────────────────────────────────────────────── - -/// Public reduced claim — same on both sides. -pub struct SampleQueriesReduced { - pub queries: QueryIndices, -} - -pub struct SampleQueriesProverOutput { - pub reduced: SampleQueriesReduced, - pub carry: (), -} - -pub struct SampleQueriesVerifierOutput { - pub reduced: SampleQueriesReduced, - pub carry: (), -} - -// ─── IOR ────────────────────────────────────────────────────────────────── - -/// Shift-query sampling phase configuration. Stateless. -pub struct SampleQueries<'a, F: Field> { - pub _phantom: PhantomData<&'a F>, -} - -impl<'a, F: Field> SampleQueries<'a, F> { - pub fn new() -> Self { - Self { - _phantom: PhantomData, - } - } -} - -impl<'a, F: Field> Default for SampleQueries<'a, F> { - fn default() -> Self { - Self::new() - } -} - -impl<'a, F> IOR for SampleQueries<'a, F> -where - F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, -{ - const NAME: &'static str = "SampleQueries"; - - type ProverInput<'b> - = SampleQueriesProverInput - where - Self: 'b; - type ProverOutput = SampleQueriesProverOutput; - type VerifierInput<'b> - = SampleQueriesVerifierInput - where - Self: 'b; - type VerifierOutput = SampleQueriesVerifierOutput; - - #[tracing::instrument(name = "sample_queries", skip_all, fields(t = input.t, log_n = input.log_n))] - fn prove<'b>( - &self, - transcript: &mut ProverState, - input: Self::ProverInput<'b>, - ) -> Result - where - Self: 'b, - { - let queries = QueryIndices::::sample(transcript, input.log_n, input.t); - Ok(SampleQueriesProverOutput { - reduced: SampleQueriesReduced { queries }, - carry: (), - }) - } - - #[tracing::instrument(name = "sample_queries.verify", skip_all, fields(t = input.t, log_n = input.log_n))] - fn verify<'b, 'v>( - &self, - transcript: &mut VerifierState<'v>, - input: Self::VerifierInput<'b>, - ) -> Result - where - Self: 'b, - { - let n_bytes = (input.t * input.log_n).div_ceil(8); - let bytes: Vec = (0..n_bytes) - .map(|_| transcript.verifier_message::<[u8; 1]>()[0]) - .collect(); - let queries = QueryIndices::from_squeezed_bytes(&bytes, input.log_n, input.t); - Ok(SampleQueriesVerifierOutput { - reduced: SampleQueriesReduced { queries }, - carry: (), - }) - } -} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 9d93709..55ed4d7 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,4 +1,4 @@ pub mod oracle; -pub mod iors; +pub mod phases; pub mod query; pub mod transcript; diff --git a/src/protocol/iors/batching.rs b/src/protocol/phases/batching.rs similarity index 60% rename from src/protocol/iors/batching.rs rename to src/protocol/phases/batching.rs index d162919..d0a0a71 100644 --- a/src/protocol/iors/batching.rs +++ b/src/protocol/phases/batching.rs @@ -11,14 +11,19 @@ //! sumcheck, with the CBBZ23 / HyperPlonk sparse-evaluation optimization //! (`accumulate_sparse_evaluations`) folded in. //! -//! IOR ports -//! --------- -//! - input (prover): `{ zeta_0, samples_flat, query_eval_points, oracle, s, t, log_n }` -//! - input (verifier): `{ zeta_0, samples_flat, query_eval_points, nus, acc_mu, s, t, log_n }` -//! - `reduced`: `{ alpha }` — same on both sides (verifier derives `alpha` -//! from the transcript) -//! - `carry` (prover): `{ mu }` — the prover's reported `\hat f(α)` -//! - verifier has no carry +//! IOR signature +//! ------------- +//! - `Statement` — `(zetas_prefix, s, t, log_n)` — shared. +//! - `Witness` — `()` +//! - `ProverInputs` — `&Oracle` (the committed oracle, full data) +//! - `VerifierInputs` — `(nus, acc_mu)` — used to compute `σ₂` and the +//! final-claim oracle check. +//! - `ReductionInputs` — `alpha` (the LSB sumcheck challenges); both sides +//! compute and feed it through `reduce_statement`. +//! - `ReducedStatement` — `alpha` — the new code-eval point (LSB-indexed) +//! - `ProofString` — `()` +//! - `ReducedWitness` — `mu` — the prover's reported `\hat f(α)` +//! - `VerifierOutputs` — `()` use ark_ff::{Field, PrimeField}; use ark_std::log2; @@ -32,7 +37,8 @@ use std::marker::PhantomData; use crate::count_ops; use crate::error::{ProverError, VerifierError}; -use crate::protocol::iors::IOR; +use crate::protocol::oracle::Oracle; +use crate::protocol::phases::IOR; use crate::protocol::transcript::EffscVerifierTranscript; use crate::utils::poly::{eq_poly, eq_poly_non_binary}; @@ -78,23 +84,24 @@ fn batched_constraint_poly( result } -// ─── Statement helper ───────────────────────────────────────────────────── +// ─── IOR signature types ────────────────────────────────────────────────── -/// Internal layout of the `1 + s + t` evaluation points used by the -/// batching sumcheck. Constructed inside `prove` / `verify` from the -/// `(zeta_0, samples_flat, query_eval_points, s, t, log_n)` tuple — single -/// source of truth so the two sides cannot drift. -struct BatchingStatement { +pub struct BatchingStatement { /// `1 + s + t` evaluation points: `[ζ_0, ood_j…, query_k…]`. - zetas_prefix: Vec>, - s: usize, - t: usize, - log_n: usize, + pub zetas_prefix: Vec>, + pub s: usize, + pub t: usize, + pub log_n: usize, + pub _phantom: std::marker::PhantomData, } impl BatchingStatement { + /// Single source of truth for the `zetas_prefix` shape — both the + /// prover and verifier orchestrators construct their `BatchingStatement` + /// through this. Drift between sides becomes structurally impossible. + /// /// Layout: `[ζ_0, ood_chunk_0, …, ood_chunk_{s-1}, query_0, …, query_{t-1}]`. - fn from_ior_outputs( + pub fn from_phase_outputs( zeta_0: Vec, ood_samples_flat: &[F], query_eval_points: &[Vec], @@ -115,60 +122,37 @@ impl BatchingStatement { s, t, log_n, + _phantom: std::marker::PhantomData, } } } -// ─── Inputs ─────────────────────────────────────────────────────────────── - -pub struct BatchingProverInput<'a, F: Field> { - pub zeta_0: &'a [F], - pub samples_flat: &'a [F], - pub query_eval_points: &'a [Vec], - pub oracle: &'a crate::protocol::oracle::Oracle, - pub s: usize, - pub t: usize, - pub log_n: usize, +pub struct BatchingProverInputs<'a, F: Field> { + pub oracle: &'a Oracle, } -pub struct BatchingVerifierInput<'a, F: Field> { - pub zeta_0: &'a [F], - pub samples_flat: &'a [F], - pub query_eval_points: &'a [Vec], +pub struct BatchingVerifierInputs { /// `1 + s + t` ν values; used to compute `σ₂ = Σ ξ_eq · ν`. pub nus: Vec, /// Multiplier on the final-claim oracle check. pub acc_mu: F, - pub s: usize, - pub t: usize, - pub log_n: usize, } -// ─── Output ports ───────────────────────────────────────────────────────── +pub struct BatchingReductionInputs { + /// LSB-indexed sumcheck challenge vector. + pub alpha: Vec, +} -/// Public reduced claim — same on both sides. -pub struct BatchingReduced { +pub struct BatchingReducedStatement { /// New code-eval point (LSB-indexed). pub alpha: Vec, } -pub struct BatchingProverCarry { +pub struct BatchingReducedWitness { /// `\hat f(α)` — prover's report. pub mu: F, } -pub struct BatchingProverOutput { - pub reduced: BatchingReduced, - pub carry: BatchingProverCarry, -} - -pub struct BatchingVerifierOutput { - pub reduced: BatchingReduced, - pub carry: (), -} - -// ─── IOR ────────────────────────────────────────────────────────────────── - /// Batching phase configuration. pub struct Batching<'a, F: Field> { pub _phantom: PhantomData<&'a F>, @@ -192,47 +176,63 @@ impl<'a, F> IOR for Batching<'a, F> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { - const NAME: &'static str = "Batching"; - - type ProverInput<'b> - = BatchingProverInput<'b, F> + type Statement = BatchingStatement; + type Witness<'b> + = () + where + Self: 'b; + type ProverInputs<'b> + = BatchingProverInputs<'b, F> where Self: 'b; - type ProverOutput = BatchingProverOutput; - type VerifierInput<'b> - = BatchingVerifierInput<'b, F> + type VerifierInputs<'b> + = BatchingVerifierInputs where Self: 'b; - type VerifierOutput = BatchingVerifierOutput; + type ReductionInputs = BatchingReductionInputs; + type ReducedStatement = BatchingReducedStatement; + type ProofString = (); + type ReducedWitness = BatchingReducedWitness; + type VerifierOutputs = (); + + fn reduce_statement( + &self, + _statement: &Self::Statement, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement { + BatchingReducedStatement { + alpha: inputs.alpha.clone(), + } + } #[tracing::instrument( name = "batching", skip_all, - fields(s = input.s, t = input.t, log_n = input.log_n) + fields(s = statement.s, t = statement.t, log_n = statement.log_n) )] - fn prove<'b>( + fn prove_inner<'b>( &self, - transcript: &mut ProverState, - input: Self::ProverInput<'b>, - ) -> Result + prover_state: &mut ProverState, + statement: &Self::Statement, + _witness: &Self::Witness<'b>, + inputs: &Self::ProverInputs<'b>, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > where - Self: 'b, + 'a: 'b, { - let statement = BatchingStatement::from_ior_outputs( - input.zeta_0.to_vec(), - input.samples_flat, - input.query_eval_points, - input.s, - input.t, - input.log_n, - ); - - let n = input.oracle.len(); + let n = inputs.oracle.len(); let r = 1 + statement.s + statement.t; let log_r = log2(r) as usize; debug_assert_eq!(statement.zetas_prefix.len(), r); - let xis = transcript.verifier_messages_vec::(log_r); + let xis = prover_state.verifier_messages_vec::(log_r); let (xi_eq_evals, ood_evals_vec) = { let _s = tracing::info_span!("batching.eq_evals").entered(); @@ -258,67 +258,60 @@ where let log_n_bits = ark_std::log2(n) as u64; count_ops!(BatchingRounds, log_n_bits); let mut ip = InnerProductProver::new( - input.oracle.evals().to_vec(), + inputs.oracle.evals().to_vec(), batched_constraint_poly(&ood_evals_vec, &id_non_0_eval_sums), ); let mut challenges = - sumcheck(&mut ip, log_n_bits as usize, transcript, noop_hook).challenges; + sumcheck(&mut ip, log_n_bits as usize, prover_state, noop_hook).challenges; challenges.reverse(); challenges }; - let mu = input.oracle.query_at_point(&alpha); + let mu = inputs.oracle.query_at_point(&alpha); - Ok(BatchingProverOutput { - reduced: BatchingReduced { + Ok(( + BatchingReductionInputs { alpha: alpha.clone(), }, - carry: BatchingProverCarry { mu }, - }) + (), + BatchingReducedWitness { mu }, + )) } #[tracing::instrument( name = "batching.verify", skip_all, - fields(s = input.s, t = input.t, log_n = input.log_n) + fields(s = statement.s, t = statement.t, log_n = statement.log_n) )] - fn verify<'b, 'v>( + fn verify_inner<'b, 'c>( &self, - transcript: &mut VerifierState<'v>, - input: Self::VerifierInput<'b>, - ) -> Result + verifier_state: &mut VerifierState<'b>, + statement: &Self::Statement, + inputs: &Self::VerifierInputs<'c>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> where - Self: 'b, + 'a: 'c, { - let statement = BatchingStatement::from_ior_outputs( - input.zeta_0.to_vec(), - input.samples_flat, - input.query_eval_points, - input.s, - input.t, - input.log_n, - ); - let r = 1 + statement.s + statement.t; let log_r = log2(r) as usize; debug_assert_eq!(statement.zetas_prefix.len(), r); - debug_assert_eq!(input.nus.len(), r); + debug_assert_eq!(inputs.nus.len(), r); // Squeeze ξ matching the prover. let xis: Vec = (0..log_r) - .map(|_| transcript.verifier_message::()) + .map(|_| verifier_state.verifier_message::()) .collect(); let xi_eq_evals = (0..r).map(|i| eq_poly(&xis, i)).collect::>(); // σ₂ = Σ ξ_eq · ν. let sigma_2 = xi_eq_evals .iter() - .zip(&input.nus) + .zip(&inputs.nus) .fold(F::zero(), |acc, (xi_eq, nu)| acc + *xi_eq * nu); // Run sumcheck_verify and check the final-claim oracle check. let res = { - let mut wrap = EffscVerifierTranscript(transcript); + let mut wrap = EffscVerifierTranscript(verifier_state); sumcheck_verify(sigma_2, 2, statement.log_n, &mut wrap, |_, _| Ok(()))? }; let alpha_lsb: Vec = res.challenges.iter().rev().copied().collect(); @@ -327,18 +320,13 @@ where for zeta in &statement.zetas_prefix { zeta_eqs.push(eq_poly_non_binary(zeta, &alpha_lsb)); } - let expected = input.acc_mu + let expected = inputs.acc_mu * zeta_eqs .into_iter() .zip(&xi_eq_evals) .fold(F::zero(), |acc, (a, b)| acc + a * *b); - (expected == res.final_claim) - .then_some(()) - .ok_or(VerifierError::Target)?; + (expected == res.final_claim).then_some(()).ok_or(VerifierError::Target)?; - Ok(BatchingVerifierOutput { - reduced: BatchingReduced { alpha: alpha_lsb }, - carry: (), - }) + Ok((BatchingReductionInputs { alpha: alpha_lsb }, ())) } } diff --git a/src/protocol/iors/bridge.rs b/src/protocol/phases/bridge.rs similarity index 50% rename from src/protocol/iors/bridge.rs rename to src/protocol/phases/bridge.rs index 0d2c6c8..07c5d05 100644 --- a/src/protocol/iors/bridge.rs +++ b/src/protocol/phases/bridge.rs @@ -21,18 +21,20 @@ //! //! Modeled as an IOR for uniformity with the rest of the choreography, //! even though it has no verifier challenges — Bridge is "0-round, -//! prover sends three values, verifier checks deferred." +//! prover sends three values, verifier checks deferred." Same shape as +//! the IOR trait, just with degenerate fields where they don't apply. //! -//! IOR ports -//! --------- -//! - input (prover): `{ zeta_0, beta_tau, z, f, bundled_pesat, hasher, -//! code_len, log_m, n_minus_k }` -//! - input (verifier): `{ gamma, deferred }` -//! - `reduced`: `{ eta, nu_0, td_new_root }` — same on both sides -//! - `carry` (prover): `{ td_new, new_x, new_w }` — feeds the next -//! accumulator -//! - verifier has no carry — `discharge` consumes the deferred check -//! in-place +//! IOR signature +//! ------------- +//! - `Statement` — `(ζ₀, β_τ, log_m, n_minus_k)` +//! - `Witness` — `(z, f)` — prover's TC-side reduced state, borrowed +//! - `ProverInputs` — `(bundled_pesat, hasher, code_len)` — config refs +//! - `VerifierInputs` — `(deferred, γ)` — what's needed to discharge TC +//! - `ReductionInputs` — `(η, ν₀, td_new_root)` — transcript-published values +//! - `ReducedStatement` — same as `ReductionInputs` (the publish is the reduction) +//! - `ProofString` — `()` (everything goes through transcript writes / reads) +//! - `ReducedWitness` — `(td_new, new_x, new_w)` — fed into the new accumulator +//! - `VerifierOutputs` — `()` use ark_ff::Field; use ark_mt::MerkleHasher; @@ -44,44 +46,52 @@ use crate::count_ops; use crate::crypto::merkle::{warp_scheme, WarpCommitted}; use crate::error::{ProverError, VerifierError}; use crate::protocol::oracle::Oracle; -use crate::protocol::iors::twin_constraint::DeferredOracleCheck; -use crate::protocol::iors::IOR; +use crate::protocol::phases::twin_constraint::DeferredOracleCheck; +use crate::protocol::phases::IOR; use crate::relations::BundledPESAT; -// ─── Inputs ─────────────────────────────────────────────────────────────── +pub struct BridgeStatement { + pub zeta_0: Vec, + pub beta_tau: Vec, + pub log_m: usize, + pub n_minus_k: usize, +} -pub struct BridgeProverInput<'a, F, P, H> +pub struct BridgeWitness<'a, F: Field> { + pub z: &'a [F], + pub f: &'a Oracle, +} + +pub struct BridgeProverInputs<'a, F, P, H> where F: Field, P: BundledPESAT, H: MerkleHasher>, { - pub zeta_0: &'a [F], - pub beta_tau: &'a [F], - pub z: &'a [F], - pub f: &'a Oracle, pub bundled_pesat: &'a P, pub hasher: &'a H, pub code_len: usize, - pub log_m: usize, - pub n_minus_k: usize, + pub _f: PhantomData, } -pub struct BridgeVerifierInput<'a, F: Field> { - pub gamma: &'a [F], +pub struct BridgeVerifierInputs<'a, F: Field> { pub deferred: &'a DeferredOracleCheck, + pub gamma: &'a [F], } -// ─── Output ports ───────────────────────────────────────────────────────── +pub struct BridgeReductionInputs { + pub eta: F, + pub nu_0: F, + pub td_new_root: H::Digest, +} -/// Public reduced claim — same on both sides. -pub struct BridgeReduced { +pub struct BridgeReducedStatement { pub eta: F, pub nu_0: F, pub td_new_root: H::Digest, } -pub struct BridgeProverCarry +pub struct BridgeReducedWitness where F: Field, H: MerkleHasher>, @@ -91,25 +101,9 @@ where pub new_w: Vec, } -pub struct BridgeProverOutput -where - F: Field, - H: MerkleHasher>, -{ - pub reduced: BridgeReduced, - pub carry: BridgeProverCarry, -} - -pub struct BridgeVerifierOutput { - pub reduced: BridgeReduced, - pub carry: (), -} - -// ─── IOR ────────────────────────────────────────────────────────────────── - /// TC → OOD bridge phase configuration. Stateless; the lifetime -/// parameter exists only to anchor `ProverInput<'a>` for the IOR trait -/// impl. +/// parameter exists only to anchor `ProverInputs<'a>` / `Witness<'a>` +/// for the IOR trait impl. pub struct Bridge<'a, F, P, H> where F: Field, @@ -150,44 +144,71 @@ where H: MerkleHasher>, H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize + Clone, { - const NAME: &'static str = "Bridge"; - - type ProverInput<'b> - = BridgeProverInput<'b, F, P, H> + type Statement = BridgeStatement; + type Witness<'b> + = BridgeWitness<'b, F> + where + Self: 'b; + type ProverInputs<'b> + = BridgeProverInputs<'b, F, P, H> where Self: 'b; - type ProverOutput = BridgeProverOutput; - type VerifierInput<'b> - = BridgeVerifierInput<'b, F> + type VerifierInputs<'b> + = BridgeVerifierInputs<'b, F> where Self: 'b; - type VerifierOutput = BridgeVerifierOutput; + type ReductionInputs = BridgeReductionInputs; + type ReducedStatement = BridgeReducedStatement; + type ProofString = (); + type ReducedWitness = BridgeReducedWitness; + type VerifierOutputs = (); + + fn reduce_statement( + &self, + _statement: &Self::Statement, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement { + BridgeReducedStatement { + eta: inputs.eta, + nu_0: inputs.nu_0, + td_new_root: inputs.td_new_root.clone(), + } + } #[tracing::instrument( name = "bridge", skip_all, - fields(log_m = input.log_m, n_minus_k = input.n_minus_k) + fields(log_m = statement.log_m, n_minus_k = statement.n_minus_k) )] - fn prove<'b>( + fn prove_inner<'b>( &self, - transcript: &mut ProverState, - input: Self::ProverInput<'b>, - ) -> Result + prover_state: &mut ProverState, + statement: &Self::Statement, + witness: &Self::Witness<'b>, + inputs: &Self::ProverInputs<'b>, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > where Self: 'b, { // η = ⟨ eq(β_τ), p(z) ⟩ - let beta_eq_evals = compute_hypercube_eq_evals(input.log_m, input.beta_tau); - let eta = input + let beta_eq_evals = compute_hypercube_eq_evals(statement.log_m, &statement.beta_tau); + let eta = inputs .bundled_pesat - .evaluate_bundled(&beta_eq_evals, input.z) + .evaluate_bundled(&beta_eq_evals, witness.z) .map_err(|_| ProverError::SpongeFish)?; // ν₀ = f̂(ζ₀) - let nu_0 = input.f.query_at_point(input.zeta_0); + let nu_0 = witness.f.query_at_point(&statement.zeta_0); // (new_x, new_w) = z[..N-k], z[N-k..] - let (new_x_slice, new_w_slice) = input.z.split_at(input.n_minus_k); + let (new_x_slice, new_w_slice) = witness.z.split_at(statement.n_minus_k); let new_x = new_x_slice.to_vec(); let new_w = new_w_slice.to_vec(); @@ -195,54 +216,56 @@ where let td_new = { let _s = tracing::info_span!("bridge.commit_new_oracle").entered(); count_ops!(MerkleTreeBuilds); - let scheme = warp_scheme::(input.hasher.clone(), input.code_len); - scheme.commit(&[input.f.evals().to_vec()]) + let scheme = warp_scheme::(inputs.hasher.clone(), inputs.code_len); + scheme.commit(&[witness.f.evals().to_vec()]) }; let td_new_root = td_new.root().clone(); // Absorb (td_new.root, η, ν₀) - transcript.prover_message(&td_new_root); - transcript.prover_message(&eta); - transcript.prover_message(&nu_0); + prover_state.prover_message(&td_new_root); + prover_state.prover_message(&eta); + prover_state.prover_message(&nu_0); - Ok(BridgeProverOutput { - reduced: BridgeReduced { + Ok(( + BridgeReductionInputs { eta, nu_0, td_new_root, }, - carry: BridgeProverCarry { + (), + BridgeReducedWitness { td_new, new_x, new_w, }, - }) + )) } #[tracing::instrument(name = "bridge.verify", skip_all)] - fn verify<'b, 'v>( + fn verify_inner<'b, 'c>( &self, - transcript: &mut VerifierState<'v>, - input: Self::VerifierInput<'b>, - ) -> Result + verifier_state: &mut VerifierState<'b>, + _statement: &Self::Statement, + inputs: &Self::VerifierInputs<'c>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> where - Self: 'b, + Self: 'c, { // Read (td_digest, η, ν₀) from the transcript. - let td_new_root: H::Digest = transcript.prover_message()?; - let eta: F = transcript.prover_message()?; - let nu_0: F = transcript.prover_message()?; + let td_new_root: H::Digest = verifier_state.prover_message()?; + let eta: F = verifier_state.prover_message()?; + let nu_0: F = verifier_state.prover_message()?; // Discharge TC's deferred check. - input.deferred.discharge(input.gamma, nu_0, eta)?; + inputs.deferred.discharge(inputs.gamma, nu_0, eta)?; - Ok(BridgeVerifierOutput { - reduced: BridgeReduced { + Ok(( + BridgeReductionInputs { eta, nu_0, td_new_root, }, - carry: (), - }) + (), + )) } } diff --git a/src/protocol/phases/mod.rs b/src/protocol/phases/mod.rs new file mode 100644 index 0000000..2680e4e --- /dev/null +++ b/src/protocol/phases/mod.rs @@ -0,0 +1,184 @@ +//! Warp IOR phases as first-class modules. +//! +//! Paired spec: `docs/paper-mods/mod1_oracle.tex` (composition rule) and the +//! forthcoming `docs/paper-mods/mod3_accumulator_state.tex`. +//! +//! Each submodule implements one Interactive Oracle Reduction from the Warp +//! construction. The [`IOR`] trait below names the paper-level components +//! and splits oracle types into prover-side / verifier-side halves so the +//! trait can serve both roles against the same struct. +//! +//! Implementor pattern: a phase is a struct holding setup parameters +//! (codes, hashers); implementors write [`IOR::prove_inner`], +//! [`IOR::verify_inner`], and [`IOR::reduce_statement`]. The trait provides +//! default `prove` / `verify` that mechanically chain +//! `*_inner -> reduce_statement` so the prover and verifier *cannot* drift +//! on how `ReducedStatement` is computed from transcript data. +//! +//! The top-level orchestrators in `src/lib.rs::WARP::prove` and `::verify` +//! thread state between phases by chaining `IOR::prove` / `IOR::verify` +//! calls — each phase's `ReducedStatement`, `ProofString`, and +//! `ReducedWitness` feed the next phase's `Statement` / `ProverInputs` / +//! the global proof object. + +// `prove_inner` returns the 3-tuple `(ReductionInputs, ProofString, +// ReducedWitness)`; clippy flags the resulting type as "complex" but +// the structure is exactly what the IOR formalism asks for. +#![allow(clippy::type_complexity)] + +pub mod batching; +pub mod bridge; +pub mod ood; +pub mod oracle_handle; +pub mod pesat; +pub mod proximity; +pub mod sample_queries; +pub mod twin_constraint; + +use spongefish::{ProverState, VerifierState}; + +use crate::error::{ProverError, VerifierError}; + +/// Interactive Oracle Reduction. +/// +/// Mirrors the IOR signature from `docs/paper-mods/mod1_oracle.tex` §4: +/// +/// ```text +/// (stmt, wit, oracles_in) --> (stmt', oracles_out) +/// ``` +/// +/// **Convention.** `Witness` and `ProverInputs` are conventionally +/// reference-holding wrappers (e.g. `PesatWitness<'a, F> { witnesses: +/// &'a [Vec] }`). The trait takes them by `&` so the caller never has +/// to clone bulk data on the way in. None of WARP / WHIR / STIR / FRI / +/// sumcheck-based protocols consume their witness at the IOR level. +/// +/// **Drift prevention.** Implementors write [`prove_inner`], +/// [`verify_inner`], and [`reduce_statement`]. The default `prove` / +/// `verify` chain them automatically; the `ReducedStatement` is computed +/// in exactly one place (`reduce_statement`), eliminating the bug class +/// where prover and verifier compute the same reduction via parallel +/// code paths that silently diverge. +pub trait IOR { + /// Pre-reduction claim. Visible to prover and verifier. + type Statement; + /// Prover-only data the prover reads. Lifetime-parameterized so a + /// pipeline composition can borrow inter-phase data into the witness + /// for one `prove` call without the borrow leaking into the phase + /// struct's lifetime — see `crate::protocol::composition`. + type Witness<'a> + where + Self: 'a; + /// Oracles flowing in from upstream IORs (prover view: full data). + /// Lifetime-parameterized for the same reason as `Witness`. + type ProverInputs<'a> + where + Self: 'a; + /// Oracles flowing in from upstream IORs (verifier view: commitments). + type VerifierInputs<'a> + where + Self: 'a; + /// Explicit inputs that determine [`Self::ReducedStatement`]. Both + /// prover-side and verifier-side machinery produce this struct, then + /// hand it to [`Self::reduce_statement`] for the (single) statement + /// derivation. + type ReductionInputs; + /// Post-reduction claim. Visible to prover and verifier. Computed + /// exclusively by [`Self::reduce_statement`]. + type ReducedStatement; + /// Out-of-band proof bytes the verifier always reads (auth paths, + /// shift-query answers, …). The orchestrator collects these into the + /// global proof object. + type ProofString; + /// Reduced-witness handoff to the next IOR — full prover-side data + /// (oracles, witness vectors, committed trees). In terminal-round + /// protocols (e.g. WHIR's final fold) this can also be revealed to + /// the verifier; the trait does not assume it is private. + type ReducedWitness; + /// Verifier-side outputs (commitments, parsed digests). Threaded into + /// downstream phase `VerifierInputs`. + type VerifierOutputs; + + /// THE single source of truth for `ReducedStatement`. + /// + /// Both prover and verifier call this with their respective + /// [`Self::ReductionInputs`] (assembled in `prove_inner` / + /// `verify_inner`). Drift between sides is structurally impossible + /// because both go through this function. + fn reduce_statement( + &self, + statement: &Self::Statement, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement; + + /// Implementor's prover-side body. Runs the protocol's prover + /// machinery and returns the inputs `reduce_statement` needs, plus + /// the proof string and reduced witness. + fn prove_inner<'a>( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + witness: &Self::Witness<'a>, + inputs: &Self::ProverInputs<'a>, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > + where + Self: 'a; + + /// Implementor's verifier-side body. Runs the protocol's verifier + /// machinery (sumcheck checks, transcript reads, soundness checks) + /// and returns the inputs `reduce_statement` needs. + fn verify_inner<'a, 'b>( + &self, + verifier_state: &mut VerifierState<'a>, + statement: &Self::Statement, + inputs: &Self::VerifierInputs<'b>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + where + Self: 'b; + + /// Default impl. Implementors should not override. + fn prove<'a>( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + witness: &Self::Witness<'a>, + inputs: &Self::ProverInputs<'a>, + ) -> Result< + ( + Self::ReducedStatement, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > + where + Self: 'a, + { + let (red_inputs, proof, red_wit) = + self.prove_inner(prover_state, statement, witness, inputs)?; + let reduced = self.reduce_statement(statement, &red_inputs); + Ok((reduced, proof, red_wit)) + } + + /// Default impl. Implementors should not override. + fn verify<'a, 'b>( + &self, + verifier_state: &mut VerifierState<'a>, + statement: &Self::Statement, + inputs: &Self::VerifierInputs<'b>, + ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> + where + Self: 'b, + { + let (red_inputs, vouts) = self.verify_inner(verifier_state, statement, inputs)?; + let reduced = self.reduce_statement(statement, &red_inputs); + Ok((reduced, vouts)) + } +} diff --git a/src/protocol/phases/ood.rs b/src/protocol/phases/ood.rs new file mode 100644 index 0000000..a799357 --- /dev/null +++ b/src/protocol/phases/ood.rs @@ -0,0 +1,167 @@ +//! Out-of-domain sampling phase. +//! +//! Paired spec: `docs/paper-mods/mod1_oracle.tex`. This phase is a thin +//! composition of point queries on the committed oracle — see +//! [`Oracle::query_at_point`](crate::protocol::oracle::Oracle::query_at_point). +//! The verifier derives the same random points from the transcript. +//! +//! IOR signature +//! ------------- +//! - `Statement` — `(s, log_n)` +//! - `Witness` — `()` +//! - `ProverInputs` — `&Oracle` (the committed oracle, full data) +//! - `VerifierInputs` — `()` (the oracle check is deferred to the batching +//! sumcheck's final claim) +//! - `ReductionInputs` — `(samples_flat, answers)` — both sides arrive here +//! from the same transcript reads and feed into `reduce_statement`. +//! - `ReducedStatement` — `(samples_flat, answers)` — query points + their answers +//! - `ProofString` — `()` +//! - `ReducedWitness` — `()` +//! - `VerifierOutputs` — `()` + +use ark_ff::{Field, PrimeField}; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; +use std::marker::PhantomData; + +use crate::count_ops; +use crate::error::{ProverError, VerifierError}; +use crate::protocol::oracle::Oracle; +use crate::protocol::phases::IOR; + +pub struct OodStatement { + pub s: usize, + pub log_n: usize, +} + +pub struct OodProverInputs<'a, F: Field> { + pub oracle: &'a Oracle, +} + +pub struct OodReductionInputs { + pub samples_flat: Vec, + pub answers: Vec, +} + +pub struct OodReducedStatement { + /// Flat challenge vector of length `s · log_n`. + pub samples_flat: Vec, + /// Answers `\hat f(ζ_j)` for each of the `s` chunked challenges. + pub answers: Vec, +} + +/// OOD phase configuration. Stateless; the lifetime parameter exists only +/// to anchor `ProverInputs<'a>` for the trait impl. +pub struct Ood<'a, F: Field> { + pub _phantom: PhantomData<&'a F>, +} + +impl<'a, F: Field> Ood<'a, F> { + pub fn new() -> Self { + Self { + _phantom: PhantomData, + } + } +} + +impl<'a, F: Field> Default for Ood<'a, F> { + fn default() -> Self { + Self::new() + } +} + +impl<'a, F> IOR for Ood<'a, F> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, +{ + type Statement = OodStatement; + type Witness<'b> + = () + where + Self: 'b; + type ProverInputs<'b> + = OodProverInputs<'b, F> + where + Self: 'b; + type VerifierInputs<'b> + = () + where + Self: 'b; + type ReductionInputs = OodReductionInputs; + type ReducedStatement = OodReducedStatement; + type ProofString = (); + type ReducedWitness = (); + type VerifierOutputs = (); + + fn reduce_statement( + &self, + _statement: &Self::Statement, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement { + OodReducedStatement { + samples_flat: inputs.samples_flat.clone(), + answers: inputs.answers.clone(), + } + } + + #[tracing::instrument(name = "ood", skip_all, fields(s = statement.s, log_n = statement.log_n))] + fn prove_inner<'b>( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + _witness: &Self::Witness<'b>, + inputs: &Self::ProverInputs<'b>, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > + where + 'a: 'b, + { + let samples_flat = prover_state.verifier_messages_vec::(statement.s * statement.log_n); + count_ops!(OodPointQueries, statement.s as u64); + let answers = samples_flat + .chunks(statement.log_n) + .map(|zeta| inputs.oracle.query_at_point(zeta)) + .collect::>(); + prover_state.prover_messages(&answers); + Ok(( + OodReductionInputs { + samples_flat, + answers, + }, + (), + (), + )) + } + + #[tracing::instrument( + name = "ood.verify", + skip_all, + fields(s = statement.s, log_n = statement.log_n) + )] + fn verify_inner<'b, 'c>( + &self, + verifier_state: &mut VerifierState<'b>, + statement: &Self::Statement, + _inputs: &Self::VerifierInputs<'c>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + where + 'a: 'c, + { + let samples_flat: Vec = (0..statement.s * statement.log_n) + .map(|_| verifier_state.verifier_message::()) + .collect(); + let answers: Vec = verifier_state.prover_messages_vec(statement.s)?; + Ok(( + OodReductionInputs { + samples_flat, + answers, + }, + (), + )) + } +} diff --git a/src/protocol/iors/oracle_handle.rs b/src/protocol/phases/oracle_handle.rs similarity index 100% rename from src/protocol/iors/oracle_handle.rs rename to src/protocol/phases/oracle_handle.rs diff --git a/src/protocol/phases/pesat.rs b/src/protocol/phases/pesat.rs new file mode 100644 index 0000000..8504832 --- /dev/null +++ b/src/protocol/phases/pesat.rs @@ -0,0 +1,209 @@ +//! PESAT Reduction phase. +//! +//! Implements Phase 2 of the Warp prover: encode fresh witnesses into +//! codewords, commit via a multi-vector Merkle tree (one root over all +//! l1 codewords), absorb commitment + code evaluations, and derive the +//! τ zero-check challenges. +//! +//! IOR signature +//! ------------- +//! - `Statement` — `(l1, log_m)` +//! - `Witness` — `&[Vec]` (fresh witnesses to encode) +//! - `ProverInputs` — `()` +//! - `VerifierInputs` — `()` +//! - `ReductionInputs` — `(mus, taus)` — both sides derive from transcript +//! - `ReducedStatement` — `(mus, taus)` — code-eval claims + zero-check randomness +//! - `ProofString` — `()` +//! - `ReducedWitness` — full codewords + multi-vector commit +//! - `VerifierOutputs` — Merkle root only + +use ark_codes::traits::LinearCode; +use ark_ff::{Field, PrimeField}; +use ark_mt::MerkleHasher; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; +use std::marker::PhantomData; + +use crate::count_ops; +use crate::crypto::merkle::{encode_codewords, warp_scheme, WarpCommitted}; +use crate::error::{ProverError, VerifierError}; +use crate::protocol::phases::IOR; + +pub struct PesatStatement { + pub l1: usize, + pub log_m: usize, +} + +pub struct PesatWitness<'a, F: Field> { + pub witnesses: &'a [Vec], +} + +pub struct PesatReductionInputs { + pub mus: Vec, + pub taus: Vec>, +} + +pub struct PesatReducedStatement { + pub mus: Vec, + pub taus: Vec>, +} + +pub struct PesatReducedWitness +where + F: Field, + H: MerkleHasher>, +{ + pub codewords: Vec>, + pub td_0: WarpCommitted, +} + +pub struct PesatVerifierOutputs { + pub rt_0: H::Digest, +} + +/// PESAT phase configuration. +pub struct Pesat<'a, F, C, H> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + C: LinearCode, + H: MerkleHasher>, +{ + pub code: &'a C, + pub hasher: &'a H, + pub _phantom: PhantomData<(F, H)>, +} + +impl<'a, F, C, H> IOR for Pesat<'a, F, C, H> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + C: LinearCode, + H: MerkleHasher>, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, +{ + type Statement = PesatStatement; + type Witness<'b> + = PesatWitness<'b, F> + where + Self: 'b; + type ProverInputs<'b> + = () + where + Self: 'b; + type VerifierInputs<'b> + = () + where + Self: 'b; + type ReductionInputs = PesatReductionInputs; + type ReducedStatement = PesatReducedStatement; + type ProofString = (); + type ReducedWitness = PesatReducedWitness; + type VerifierOutputs = PesatVerifierOutputs; + + fn reduce_statement( + &self, + _statement: &Self::Statement, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement { + PesatReducedStatement { + mus: inputs.mus.clone(), + taus: inputs.taus.clone(), + } + } + + #[tracing::instrument( + name = "pesat", + skip_all, + fields(l1 = statement.l1, log_m = statement.log_m, n_witnesses = witness.witnesses.len()) + )] + fn prove_inner<'b>( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + witness: &Self::Witness<'b>, + _inputs: &Self::ProverInputs<'b>, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > + where + 'a: 'b, + H: 'b, + { + // a. encode witnesses + let codewords = { + let _s = tracing::info_span!("pesat.encode").entered(); + count_ops!(EncodeCalls, witness.witnesses.len() as u64); + encode_codewords(self.code, witness.witnesses) + }; + + // b. evaluation claims + let mus = codewords.iter().map(|f| f[0]).collect::>(); + + // c. commit to interleaved codewords (multi-vector commitment) + let td_0 = { + let _s = tracing::info_span!("pesat.merkle_commit").entered(); + count_ops!(MerkleTreeBuilds); + let scheme = warp_scheme(self.hasher.clone(), self.code.code_len()); + scheme.commit(&codewords) + }; + + // d. absorb commitment + claims; e/f. derive τ challenges. + let taus = { + let _s = tracing::info_span!("pesat.absorb_and_derive").entered(); + prover_state.prover_message(td_0.root()); + prover_state.prover_messages(&mus); + + (0..statement.l1) + .map(|_| prover_state.verifier_messages_vec::(statement.log_m)) + .collect::>() + }; + + Ok(( + PesatReductionInputs { + mus: mus.clone(), + taus, + }, + (), + PesatReducedWitness { codewords, td_0 }, + )) + } + + #[tracing::instrument( + name = "pesat.verify", + skip_all, + fields(l1 = statement.l1, log_m = statement.log_m) + )] + fn verify_inner<'b, 'c>( + &self, + verifier_state: &mut VerifierState<'b>, + statement: &Self::Statement, + _inputs: &Self::VerifierInputs<'c>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + where + 'a: 'c, + H: 'c, + { + // commitment digest + let rt_0: H::Digest = verifier_state.prover_message()?; + + // mus (l1 evaluation claims) + let mus: Vec = verifier_state.prover_messages_vec(statement.l1)?; + + // taus (l1 zero-check challenge vectors) + let taus: Vec> = (0..statement.l1) + .map(|_| { + (0..statement.log_m) + .map(|_| verifier_state.verifier_message::()) + .collect() + }) + .collect(); + + Ok(( + PesatReductionInputs { mus, taus }, + PesatVerifierOutputs { rt_0 }, + )) + } +} diff --git a/src/protocol/phases/proximity.rs b/src/protocol/phases/proximity.rs new file mode 100644 index 0000000..5ce6eaf --- /dev/null +++ b/src/protocol/phases/proximity.rs @@ -0,0 +1,269 @@ +//! Proximity / shift-query phase. +//! +//! Index queries on the committed oracles. Opens both the fresh PESAT +//! commitment and each accumulated commitment at the query positions, +//! producing one multi-opening proof per commitment and a flat table of +//! the codeword values at those positions. +//! +//! IOR signature +//! ------------- +//! - `Statement` — `(queries, l2, t, n)`. `n` is the codeword length; +//! the verifier needs it to construct the `WarpScheme` used for `check`. +//! - `Witness` — `()` +//! - `ProverInputs` — fresh `WarpCommitted` + l2 accumulated `WarpCommitted`s +//! - `VerifierInputs` — fresh root + l2 acc roots + opening proofs + answers +//! - `ReductionInputs` — `()` (no reduction) +//! - `ReducedStatement` — `()` (Proximity is a check, not a reduction) +//! - `ProofString` — opening proofs + shift_query_answers +//! - `ReducedWitness` — `()` +//! - `VerifierOutputs` — `()` + +use ark_ff::Field; +use ark_mt::MerkleHasher; +use spongefish::{ProverState, VerifierState}; +use std::marker::PhantomData; + +use crate::count_ops; +use crate::crypto::merkle::{warp_scheme, WarpCommitted, WarpProof}; +use crate::error::{ProverError, VerifierError}; +use crate::protocol::phases::oracle_handle::IndexedOracle; +use crate::protocol::phases::IOR; +use crate::protocol::query::QueryIndices; + +pub struct ProximityStatement { + pub queries: QueryIndices, + pub l2: usize, + pub t: usize, + /// Codeword length (`code.code_len()`), needed by both prover and + /// verifier to construct the `WarpScheme` used for open/check. + pub n: usize, +} + +pub struct ProximityProverInputs<'a, F, H> +where + F: Field, + H: MerkleHasher>, +{ + pub td_0: &'a WarpCommitted, + pub acc_td: &'a [WarpCommitted], +} + +/// Verifier-side inputs for Proximity. +/// +/// The phase verifier no longer sees roots / opening proofs / answer +/// tables directly; it sees [`IndexedOracle`] handles and triggers +/// their (lazy, memoized) BCS validation. Phases stay BCS-agnostic at +/// the type level — the orchestrator picks the concrete handle type +/// (today: [`oracle_handle::MerkleIndexedOracle`]). +/// +/// Generic over `O: IndexedOracle>` so dispatch is static; no +/// trait objects. +pub struct ProximityVerifierInputs<'a, F, O> +where + F: Field, + O: IndexedOracle>, +{ + /// Handle for the fresh PESAT multi-vector commitment (m = l1). + pub fresh: &'a O, + /// One handle per accumulated commitment (each with m = 1). + pub acc: &'a [O], + pub _f: PhantomData, +} + +pub struct ProximityProofString +where + F: Field, + H: MerkleHasher, +{ + /// Single multi-opening proof for the fresh PESAT commitment. + pub auth_0: WarpProof, + /// One multi-opening proof per accumulated commitment. + pub auth_j: Vec>, + /// Shift query answers: per-query × per-codeword. + /// Outer length = t (queries). Inner length = (l2 acc + l1 fresh). + pub shift_query_answers: Vec>, +} + +/// Proximity phase configuration. Holds the hasher value used by both +/// `open` (prover side) and `check` (verifier side). +pub struct Proximity<'a, F, H> +where + F: Field, + H: MerkleHasher>, +{ + pub hasher: &'a H, + pub _phantom: PhantomData, +} + +impl<'a, F, H> IOR for Proximity<'a, F, H> +where + F: Field, + H: MerkleHasher>, +{ + type Statement = ProximityStatement; + type Witness<'b> + = () + where + Self: 'b; + type ProverInputs<'b> + = ProximityProverInputs<'b, F, H> + where + Self: 'b; + type VerifierInputs<'b> + = ProximityVerifierInputs< + 'b, + F, + crate::protocol::phases::oracle_handle::MerkleIndexedOracle<'b, F, H>, + > + where + Self: 'b; + type ReductionInputs = (); + type ReducedStatement = (); + type ProofString = ProximityProofString; + type ReducedWitness = (); + type VerifierOutputs = (); + + fn reduce_statement( + &self, + _statement: &Self::Statement, + _inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement { + } + + #[tracing::instrument( + name = "proximity", + skip_all, + fields( + n_queries = statement.queries.leaf_positions.len(), + n_accumulators = inputs.acc_td.len(), + ) + )] + fn prove_inner<'b>( + &self, + _prover_state: &mut ProverState, + statement: &Self::Statement, + _witness: &Self::Witness<'b>, + inputs: &Self::ProverInputs<'b>, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > + where + 'a: 'b, + H: 'b, + { + let leaf_positions = &statement.queries.leaf_positions; + + // ark-mt's `open()` requires strictly-sorted, unique indices. Query + // positions can repeat or arrive unsorted; deduplicate for the + // merkle opening, but keep `shift_query_answers` in original query + // order so downstream phases (batching) can index by query. + let mut sorted_unique = leaf_positions.clone(); + sorted_unique.sort_unstable(); + sorted_unique.dedup(); + + let scheme = warp_scheme(self.hasher.clone(), statement.n); + + let auth_0 = { + let _s = tracing::info_span!("proximity.auth_0").entered(); + count_ops!(MerklePathsGenerated, sorted_unique.len() as u64); + scheme.open(inputs.td_0, &sorted_unique) + }; + + let auth_j: Vec> = { + let _s = tracing::info_span!("proximity.auth_j").entered(); + count_ops!( + MerklePathsGenerated, + (inputs.acc_td.len() * sorted_unique.len()) as u64 + ); + inputs + .acc_td + .iter() + .map(|td| scheme.open(td, &sorted_unique)) + .collect() + }; + + // Shift query answers: per query position, values across + // (acc_codewords ++ fresh_codewords). + let shift_query_answers = { + let _s = tracing::info_span!("proximity.shift_queries").entered(); + let total_codewords = inputs + .acc_td + .iter() + .map(|td| td.num_codewords()) + .sum::() + + inputs.td_0.num_codewords(); + let mut answers = vec![vec![F::default(); total_codewords]; leaf_positions.len()]; + for (qi, idx) in leaf_positions.iter().enumerate() { + let mut col = 0usize; + for td in inputs.acc_td.iter() { + for cw in td.codewords() { + answers[qi][col] = cw[*idx]; + col += 1; + } + } + for cw in inputs.td_0.codewords() { + answers[qi][col] = cw[*idx]; + col += 1; + } + } + answers + }; + + Ok(( + (), + ProximityProofString { + auth_0, + auth_j, + shift_query_answers, + }, + (), + )) + } + + #[tracing::instrument( + name = "proximity.verify", + skip_all, + fields(t = statement.t, l2 = statement.l2) + )] + fn verify_inner<'b, 'c>( + &self, + _verifier_state: &mut VerifierState<'b>, + statement: &Self::Statement, + inputs: &Self::VerifierInputs<'c>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + where + 'a: 'c, + H: 'c, + { + // Arity / shape checks (orchestrator-side concerns the phase still + // owns: number of accumulator openings must match l2; number of + // shift queries is implicitly checked by the handle constructors). + (inputs.acc.len() == statement.l2).then_some(()).ok_or(VerifierError::NumL2Instances)?; + + // Validate each oracle handle. The handle is a trait-object + // partial function: validate() runs the (lazy, memoized) BCS + // check internally — this phase code is BCS-agnostic. + inputs + .fresh + .validate() + .then_some(()).ok_or(VerifierError::ShiftQuery)?; + count_ops!( + MerklePathsVerified, + statement.queries.leaf_positions.len() as u64 + ); + for handle in inputs.acc.iter() { + handle.validate().then_some(()).ok_or(VerifierError::ShiftQuery)?; + count_ops!( + MerklePathsVerified, + statement.queries.leaf_positions.len() as u64 + ); + } + + Ok(((), ())) + } +} diff --git a/src/protocol/phases/sample_queries.rs b/src/protocol/phases/sample_queries.rs new file mode 100644 index 0000000..29d04d5 --- /dev/null +++ b/src/protocol/phases/sample_queries.rs @@ -0,0 +1,135 @@ +//! Shift-query sampling phase. +//! +//! Between OOD and Batching, the verifier draws `t · log_n` random +//! bits and decodes them into `t` shift-query positions over +//! `{0, 1}^log_n`, plus their corresponding evaluation-point vectors. +//! +//! This is the simplest possible IOR — one round of pure verifier +//! randomness, no prover messages, no oracles. Modeled as an IOR for +//! uniformity with the rest of the choreography. +//! +//! IOR signature +//! ------------- +//! - `Statement` — `(log_n, t)` +//! - `Witness` — `()` +//! - `ProverInputs` — `()` +//! - `VerifierInputs` — `()` +//! - `ReductionInputs` — the sampled [`QueryIndices`] +//! - `ReducedStatement` — same — both sides receive the queries +//! - `ProofString` — `()` +//! - `ReducedWitness` — `()` (no asymmetric data: both sides hold the same queries) +//! - `VerifierOutputs` — `()` + +use ark_ff::Field; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; +use std::marker::PhantomData; + +use crate::error::{ProverError, VerifierError}; +use crate::protocol::phases::IOR; +use crate::protocol::query::QueryIndices; + +pub struct SampleQueriesStatement { + pub log_n: usize, + pub t: usize, +} + +pub struct SampleQueriesReductionInputs { + pub queries: QueryIndices, +} + +pub struct SampleQueriesReducedStatement { + pub queries: QueryIndices, +} + +/// Shift-query sampling phase configuration. Stateless. +pub struct SampleQueries<'a, F: Field> { + pub _phantom: PhantomData<&'a F>, +} + +impl<'a, F: Field> SampleQueries<'a, F> { + pub fn new() -> Self { + Self { + _phantom: PhantomData, + } + } +} + +impl<'a, F: Field> Default for SampleQueries<'a, F> { + fn default() -> Self { + Self::new() + } +} + +impl<'a, F> IOR for SampleQueries<'a, F> +where + F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, +{ + type Statement = SampleQueriesStatement; + type Witness<'b> + = () + where + Self: 'b; + type ProverInputs<'b> + = () + where + Self: 'b; + type VerifierInputs<'b> + = () + where + Self: 'b; + type ReductionInputs = SampleQueriesReductionInputs; + type ReducedStatement = SampleQueriesReducedStatement; + type ProofString = (); + type ReducedWitness = (); + type VerifierOutputs = (); + + fn reduce_statement( + &self, + _statement: &Self::Statement, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement { + SampleQueriesReducedStatement { + queries: inputs.queries.clone(), + } + } + + #[tracing::instrument(name = "sample_queries", skip_all, fields(t = statement.t, log_n = statement.log_n))] + fn prove_inner<'b>( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement, + _witness: &Self::Witness<'b>, + _inputs: &Self::ProverInputs<'b>, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > + where + Self: 'b, + { + let queries = QueryIndices::::sample(prover_state, statement.log_n, statement.t); + Ok((SampleQueriesReductionInputs { queries }, (), ())) + } + + #[tracing::instrument(name = "sample_queries.verify", skip_all)] + fn verify_inner<'b, 'c>( + &self, + verifier_state: &mut VerifierState<'b>, + statement: &Self::Statement, + _inputs: &Self::VerifierInputs<'c>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + where + Self: 'c, + { + let n_bytes = (statement.t * statement.log_n).div_ceil(8); + let bytes: Vec = (0..n_bytes) + .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) + .collect(); + let queries = QueryIndices::from_squeezed_bytes(&bytes, statement.log_n, statement.t); + Ok((SampleQueriesReductionInputs { queries }, ())) + } +} diff --git a/src/protocol/iors/twin_constraint.rs b/src/protocol/phases/twin_constraint.rs similarity index 62% rename from src/protocol/iors/twin_constraint.rs rename to src/protocol/phases/twin_constraint.rs index 7e0a5fb..2bad4a2 100644 --- a/src/protocol/iors/twin_constraint.rs +++ b/src/protocol/phases/twin_constraint.rs @@ -17,23 +17,19 @@ //! //! Each round's round polynomial has the form `h(X) = (f(X) + ω·p(X))·t(X)`. //! -//! IOR ports -//! --------- -//! - input (prover): `{ acc_instance, l1_mus, l1_taus, witnesses, -//! instances, acc_witness_w, fresh_codewords, acc_codewords, -//! log_l, log_m, log_n }` -//! - input (verifier): `{ parsed_acc, l1_mus, l1_taus, log_l, log_m, log_n }` -//! - `reduced`: `{ gamma, zeta_0, beta_tau, deferred }` — same on both -//! sides; ζ₀ and β_τ are computed from γ via [`compute_reduced`], -//! the single source of truth shared by `prove` and `verify`. -//! - `carry` (prover): `{ f, z }` — new reduced codeword oracle and the -//! reduced witness vector `z = (x, w)`, consumed by η evaluation. -//! - `carry` (verifier): none — the verifier has no oracle data to carry. -//! -//! The deferred oracle check `final_claim ≟ eq(τ, γ) · (ν₀ + ω·η)` cannot -//! complete inside `verify` because ν₀ and η arrive on the transcript -//! *after* the sumcheck rounds. The obligation is exposed as -//! [`DeferredOracleCheck`] inside `reduced` and discharged by Bridge. +//! IOR signature +//! ------------- +//! - `Statement` — accumulator instance + `l1_mus` + dimensions +//! - `Witness` — fresh witnesses + accumulator witness halves + R1CS +//! - `ProverInputs` — full codewords (from PESAT) + accumulated codewords +//! - `VerifierInputs` — `()` — the deferred oracle check (final\_claim ≟ eq(τ,γ)·(ν₀+ω·η)) +//! runs in the orchestrator after ν₀ and η arrive on the transcript +//! - `ReductionInputs` — `(ω, τ, γ, final_claim)` — both sides feed these into +//! `reduce_statement`, which computes ζ₀ / β_τ via `scale_and_sum` (single source of truth). +//! - `ReducedStatement` — sumcheck challenges γ + unchecked final_claim + ω, τ + new α (ζ₀) + new β_τ +//! - `ProofString` — `()` +//! - `ReducedWitness` — the new reduced oracle `f` + the reduced witness vector `z` +//! - `VerifierOutputs` — `()` (the new commitment is read from the transcript by the orchestrator) use ark_ff::{Field, PrimeField}; use ark_mt::MerkleHasher; @@ -53,7 +49,7 @@ use std::marker::PhantomData; use crate::count_ops; use crate::error::{ProverError, VerifierError}; use crate::protocol::oracle::Oracle; -use crate::protocol::iors::IOR; +use crate::protocol::phases::IOR; use crate::protocol::transcript::EffscVerifierTranscript; use crate::relations::r1cs::R1CSConstraints; use crate::types::AccumulatorInstance; @@ -186,32 +182,29 @@ impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { } } -// ─── Inputs ─────────────────────────────────────────────────────────────── +// ─── IOR signature types ────────────────────────────────────────────────── -pub struct TwinConstraintProverInput<'a, F: Field, H: MerkleHasher> { +pub struct TwinConstraintStatement { pub acc_instance: AccumulatorInstance, - pub l1_mus: &'a [F], - pub l1_taus: &'a [Vec], - pub witnesses: &'a [Vec], - pub instances: &'a [Vec], - pub acc_witness_w: &'a [Vec], - pub fresh_codewords: &'a [Vec], - pub acc_codewords: &'a [Vec], + pub l1_mus: Vec, + pub l1_taus: Vec>, pub log_l: usize, pub log_m: usize, pub log_n: usize, } -pub struct TwinConstraintVerifierInput<'a, F: Field, H: MerkleHasher> { - pub parsed_acc: AccumulatorInstance, - pub l1_mus: &'a [F], - pub l1_taus: &'a [Vec], - pub log_l: usize, - pub log_m: usize, - pub log_n: usize, +pub struct TwinConstraintWitness<'a, F: Field> { + pub acc_witness_w: &'a [Vec], + pub instances: &'a [Vec], + pub witnesses: &'a [Vec], } -// ─── Output ports ───────────────────────────────────────────────────────── +pub struct TwinConstraintProverInputs<'a, F: Field> { + /// Fresh codewords emitted by PESAT. + pub fresh_codewords: &'a [Vec], + /// Accumulated codewords (from the accumulator witness). + pub acc_codewords: &'a [Vec], +} /// A typed "you owe me a check" handle. /// @@ -220,8 +213,7 @@ pub struct TwinConstraintVerifierInput<'a, F: Field, H: MerkleHasher> { /// completed inside `TwinConstraint::verify` because ν₀ and η arrive on the /// transcript *after* the sumcheck rounds. Rather than splitting /// TwinConstraint into two IORs (which cascades into other phases having -/// similar shapes), we expose the obligation as a typed value carried in -/// `reduced`. +/// similar shapes), we expose the obligation as a typed value. /// /// Discharge by calling [`Self::discharge`] with the missing inputs once the /// orchestrator has read them from the transcript. @@ -239,16 +231,15 @@ impl DeferredOracleCheck { /// Discharge the deferred check. /// /// Returns `Ok(())` iff `eq(τ, γ) · (ν₀ + ω·η) == claim`. `γ` arrives via - /// the parent [`TwinConstraintReduced`]; `ν₀, η` come from the transcript - /// segment immediately following the TwinConstraint sumcheck. + /// the parent `TwinConstraintReducedStatement`; `ν₀, η` come from the + /// transcript segment immediately following the TwinConstraint sumcheck. pub fn discharge(&self, gamma: &[F], nu_0: F, eta: F) -> Result<(), VerifierError> { let expected = eq_poly_non_binary(&self.tau, gamma) * (nu_0 + self.omega * eta); (expected == self.claim).then_some(()).ok_or(VerifierError::Target) } } -/// Public reduced claim — same on both sides. -pub struct TwinConstraintReduced { +pub struct TwinConstraintReducedStatement { /// Sumcheck challenge vector (LSB-indexed). pub gamma: Vec, /// New code-evaluation point (becomes the new accumulator's α). @@ -261,70 +252,27 @@ pub struct TwinConstraintReduced { pub deferred: DeferredOracleCheck, } -pub struct TwinConstraintProverCarry { +/// Inputs to [`TwinConstraint::reduce_statement`]. Both prover and +/// verifier produce this struct from their respective machinery and feed +/// it into the shared reduction. Drift between the two sides is +/// structurally impossible because ζ₀ / β_τ are computed in exactly one +/// place — `reduce_statement` — using `scale_and_sum`. +pub struct TwinConstraintReductionInputs { + pub omega: F, + pub tau: Vec, + /// Sumcheck challenge vector (LSB-indexed). + pub gamma: Vec, + /// Sumcheck final value (the unchecked claim). + pub final_claim: F, +} + +pub struct TwinConstraintReducedWitness { /// New reduced codeword oracle. pub f: Oracle, /// Reduced witness vector `z = (x, w)` — consumed by η evaluation. pub z: Vec, } -pub struct TwinConstraintProverOutput { - pub reduced: TwinConstraintReduced, - pub carry: TwinConstraintProverCarry, -} - -pub struct TwinConstraintVerifierOutput { - pub reduced: TwinConstraintReduced, - pub carry: (), -} - -// ─── Private reduction helper ───────────────────────────────────────────── - -/// Single source of truth for ζ₀ / β_τ. Both prover and verifier land -/// here with `(ω, τ, γ, final_claim)` together with the public statement -/// pieces; the new accumulator state is computed identically on both -/// sides. -fn compute_reduced( - omega: F, - tau: Vec, - gamma: Vec, - final_claim: F, - acc_instance: &AccumulatorInstance, - l1_taus: &[Vec], - l1: usize, - log_l: usize, - log_n: usize, -) -> TwinConstraintReduced { - let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma); - - let alpha_vecs = concat_slices(&acc_instance.alpha, &vec![vec![F::zero(); log_n]; l1]); - let zeta_0 = scale_and_sum(&alpha_vecs, &gamma_eq_evals); - - // β τ-vectors: accumulated taus first (length l2), then PESAT taus - // (length l1). The τ component of the new β = Σ γ_eq(i) · β_i. - let beta_taus: Vec> = acc_instance - .beta - .0 - .iter() - .cloned() - .chain(l1_taus.iter().cloned()) - .collect(); - let beta_tau = scale_and_sum(&beta_taus, &gamma_eq_evals); - - TwinConstraintReduced { - gamma, - zeta_0, - beta_tau, - deferred: DeferredOracleCheck { - omega, - tau, - claim: final_claim, - }, - } -} - -// ─── IOR ────────────────────────────────────────────────────────────────── - /// TwinConstraint phase configuration. pub struct TwinConstraint<'a, F, H> where @@ -340,40 +288,100 @@ where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, H: MerkleHasher, { - const NAME: &'static str = "TwinConstraint"; - - type ProverInput<'b> - = TwinConstraintProverInput<'b, F, H> + type Statement = TwinConstraintStatement; + type Witness<'b> + = TwinConstraintWitness<'b, F> + where + Self: 'b; + type ProverInputs<'b> + = TwinConstraintProverInputs<'b, F> where Self: 'b; - type ProverOutput = TwinConstraintProverOutput; - type VerifierInput<'b> - = TwinConstraintVerifierInput<'b, F, H> + type VerifierInputs<'b> + = () where Self: 'b; - type VerifierOutput = TwinConstraintVerifierOutput; + type ReductionInputs = TwinConstraintReductionInputs; + type ReducedStatement = TwinConstraintReducedStatement; + type ProofString = (); + type ReducedWitness = TwinConstraintReducedWitness; + type VerifierOutputs = (); + + /// Single source of truth for ζ₀ / β_τ. Both prover and verifier + /// land here with `(ω, τ, γ, final_claim)`; the new accumulator + /// state is computed identically on both sides. + fn reduce_statement( + &self, + statement: &Self::Statement, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement { + let log_l = statement.log_l; + let log_n = statement.log_n; + let l1 = statement.l1_mus.len(); + + let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &inputs.gamma); + + let alpha_vecs = concat_slices( + &statement.acc_instance.alpha, + &vec![vec![F::zero(); log_n]; l1], + ); + let zeta_0 = scale_and_sum(&alpha_vecs, &gamma_eq_evals); + + // β τ-vectors: accumulated taus first (length l2), then PESAT taus + // (length l1). The τ component of the new β = Σ γ_eq(i) · β_i. + let beta_taus: Vec> = statement + .acc_instance + .beta + .0 + .iter() + .cloned() + .chain(statement.l1_taus.iter().cloned()) + .collect(); + let beta_tau = scale_and_sum(&beta_taus, &gamma_eq_evals); + + TwinConstraintReducedStatement { + gamma: inputs.gamma.clone(), + zeta_0, + beta_tau, + deferred: DeferredOracleCheck { + omega: inputs.omega, + tau: inputs.tau.clone(), + claim: inputs.final_claim, + }, + } + } #[tracing::instrument( name = "twin_constraint", skip_all, - fields(log_l = input.log_l, log_m = input.log_m, log_n = input.log_n) + fields(log_l = statement.log_l, log_m = statement.log_m, log_n = statement.log_n) )] - fn prove<'b>( + fn prove_inner<'b>( &self, - transcript: &mut ProverState, - input: Self::ProverInput<'b>, - ) -> Result + prover_state: &mut ProverState, + statement: &Self::Statement, + witness: &Self::Witness<'b>, + inputs: &Self::ProverInputs<'b>, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > where - Self: 'b, + 'a: 'b, + H: 'b, { - let l1 = input.fresh_codewords.len(); - let log_l = input.log_l; - let log_m = input.log_m; - let log_n = input.log_n; + let l1 = inputs.fresh_codewords.len(); + let log_l = statement.log_l; + let log_m = statement.log_m; + let log_n = statement.log_n; // a. zero-check randomness - let omega: F = transcript.verifier_message(); - let tau = transcript.verifier_messages_vec::(log_l); + let omega: F = prover_state.verifier_message(); + let tau = prover_state.verifier_messages_vec::(log_l); // b. assemble sumcheck tables let tau_eq_evals = Ascending::new(log_l) @@ -381,35 +389,35 @@ where .collect::>(); let alpha_vecs = concat_slices( - &input.acc_instance.alpha, + &statement.acc_instance.alpha, &vec![vec![F::zero(); log_n]; l1], ); - let z_vecs: Vec> = input + let z_vecs: Vec> = statement .acc_instance .beta .1 .iter() - .zip(input.acc_witness_w) - .chain(input.instances.iter().zip(input.witnesses)) + .zip(witness.acc_witness_w) + .chain(witness.instances.iter().zip(witness.witnesses)) .map(|(x, w)| concat_slices(x, w)) .collect(); // β tables: accumulated β-τs first, then PESAT τs. - let beta_vecs: Vec> = input + let beta_vecs: Vec> = statement .acc_instance .beta .0 .iter() .cloned() - .chain(input.l1_taus.iter().cloned()) + .chain(statement.l1_taus.iter().cloned()) .collect(); let tablewise = vec![ - concat_slices(input.acc_codewords, input.fresh_codewords), // u - z_vecs, // z - alpha_vecs, // a - beta_vecs, // b + concat_slices(inputs.acc_codewords, inputs.fresh_codewords), // u + z_vecs, // z + alpha_vecs, // a + beta_vecs, // b ]; let pw = vec![tau_eq_evals]; // tau @@ -425,76 +433,71 @@ where let proof = { let _s = tracing::info_span!("twin_constraint.sumcheck").entered(); count_ops!(TwinConstraintRounds, log_l as u64); - sumcheck(&mut cc, log_l, transcript, noop_hook) + sumcheck(&mut cc, log_l, prover_state, noop_hook) }; debug_assert_eq!(proof.challenges.len(), log_l); // d. pull only the reduced *witness* halves out of CC. ζ₀ and β_τ - // are NOT pulled here — `compute_reduced` recomputes them from - // (input, γ) via `scale_and_sum`, which is the single source of - // truth that both prover and verifier go through. - let reduced_tables = cc.tablewise(); - debug_assert!(reduced_tables.iter().all(|t| t.len() == 1)); - let f = reduced_tables[0][0].clone(); - let z = reduced_tables[1][0].clone(); - - let reduced = compute_reduced::( - omega, - tau, - proof.challenges, - proof.final_value, - &input.acc_instance, - input.l1_taus, - l1, - log_l, - log_n, - ); - - Ok(TwinConstraintProverOutput { - reduced, - carry: TwinConstraintProverCarry { + // are NOT pulled here — `reduce_statement` recomputes them from + // (statement, γ) via `scale_and_sum`, which is the single source + // of truth that both prover and verifier go through. + let reduced = cc.tablewise(); + debug_assert!(reduced.iter().all(|t| t.len() == 1)); + let f = reduced[0][0].clone(); + let z = reduced[1][0].clone(); + + Ok(( + TwinConstraintReductionInputs { + omega, + tau, + gamma: proof.challenges, + final_claim: proof.final_value, + }, + (), + TwinConstraintReducedWitness { f: Oracle::from_evals(f), z, }, - }) + )) } #[tracing::instrument( name = "twin_constraint.verify", skip_all, - fields(log_l = input.log_l, log_m = input.log_m, log_n = input.log_n) + fields(log_l = statement.log_l, log_m = statement.log_m, log_n = statement.log_n) )] - fn verify<'b, 'v>( + fn verify_inner<'b, 'c>( &self, - transcript: &mut VerifierState<'v>, - input: Self::VerifierInput<'b>, - ) -> Result + verifier_state: &mut VerifierState<'b>, + statement: &Self::Statement, + _inputs: &Self::VerifierInputs<'c>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> where - Self: 'b, + 'a: 'c, + H: 'c, { - let log_l = input.log_l; - let log_m = input.log_m; - let log_n = input.log_n; - let l1 = input.l1_mus.len(); + let log_l = statement.log_l; + let log_n = statement.log_n; + let l1 = statement.l1_mus.len(); // Squeeze ω, τ matching the prover. - let omega: F = transcript.verifier_message(); + let omega: F = verifier_state.verifier_message(); let tau: Vec = (0..log_l) - .map(|_| transcript.verifier_message::()) + .map(|_| verifier_state.verifier_message::()) .collect(); // Compute σ₁ = Σ_i τ_eq(i) · (μ_i + ω · η_i). let tau_eq_evals = compute_hypercube_eq_evals(log_l, &tau); - let etas_l2_first = concat_slices(&input.parsed_acc.eta, &vec![F::zero(); l1]); + let etas_l2_first = concat_slices(&statement.acc_instance.eta, &vec![F::zero(); l1]); let sigma_1 = tau_eq_evals .into_iter() .zip( - input - .parsed_acc + statement + .acc_instance .mu .iter() .copied() - .chain(input.l1_mus.iter().copied()) + .chain(statement.l1_mus.iter().copied()) .zip(etas_l2_first), ) .fold(F::zero(), |acc, (eq_tau, (mu, eta))| { @@ -505,25 +508,21 @@ where // final_claim == eq(τ, γ) · (ν₀ + ω · η) // is left to the orchestrator because ν₀ and η arrive on the // transcript AFTER the sumcheck rounds. - let tc_degree = 1 + (log_n + 1).max(log_m + 2); + let tc_degree = 1 + (log_n + 1).max(statement.log_m + 2); let (gamma, final_claim) = { - let mut wrap = EffscVerifierTranscript(transcript); + let mut wrap = EffscVerifierTranscript(verifier_state); let res = sumcheck_verify(sigma_1, tc_degree, log_l, &mut wrap, |_, _| Ok(()))?; (res.challenges, res.final_claim) }; - let reduced = compute_reduced::( - omega, - tau, - gamma, - final_claim, - &input.parsed_acc, - input.l1_taus, - l1, - log_l, - log_n, - ); - - Ok(TwinConstraintVerifierOutput { reduced, carry: () }) + Ok(( + TwinConstraintReductionInputs { + omega, + tau, + gamma, + final_claim, + }, + (), + )) } } From 22ae199cea98cf8edcc5d91b6d27155bdd8f39b8 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 12 May 2026 14:45:10 +0200 Subject: [PATCH 55/68] ior: restore formal trait + add prove_ior!/verify_ior! macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts the trait collapse from 94ac227. The IOR trait is back to its formal 9-type shape — Statement, Witness, ProverInputs, VerifierInputs, ReductionInputs, ReducedStatement, ProofString, ReducedWitness, VerifierOutputs — plus `reduce_statement` for the single-source-of-truth reduction. That's what an IOR actually is, in the paper sense. The prior collapse made it just "endpoint protocol with prover/verifier inputs/outputs" — not an IOR. Additions on top of the restored trait: 1. `const NAME: &'static str;` on the trait. Each IOR labels itself. 2. `Statement` is now a GAT: `type Statement<'a> where Self: 'a;`. Allows statements to borrow from upstream-IOR outputs (none do today; the GAT just removes the future barrier). 3. Helper structs + macros for ergonomic call sites: - `IorProveResult { reduced, proof, witness }` - `IorVerifyResult { reduced, outputs }` - `prove_ior!` / `verify_ior!` macros wrapping `IOR::prove` / `IOR::verify` tuple returns into the named-field structs. The choreography in `WARP::prove` / `WARP::verify` now reads: let IorProveResult { reduced: PesatReducedStatement { mus, taus }, proof: _, witness: PesatReducedWitness { codewords, td_0 }, } = prove_ior!( pesat_ior, prover_state, statement: PesatStatement { l1, log_m }, witness: PesatWitness { witnesses: &witnesses }, inputs: (), )?; Every paper concept is named — statement, witness, inputs, reduced, proof, witness. The IOR trait stays formal. The macro hides only the tuple-packaging syntax, not the reduction shape. Bridge and SampleQueries stay as IORs with the full formal trait implemented (degenerate but legitimate — Bridge's "proof string" is its transcript writes; SampleQueries' "witness" is `()`). Renames `src/protocol/phases/` → `src/protocol/iors/` and updates all imports. Variable names use the `_ior` suffix throughout. Tests transcript-identical: 22 lib + 2 integration + 9 verifier-negative all pass. --- src/lib.rs | 289 ++++++++++-------- src/protocol/{phases => iors}/batching.rs | 24 +- src/protocol/{phases => iors}/bridge.rs | 42 +-- src/protocol/{phases => iors}/mod.rs | 113 ++++++- src/protocol/{phases => iors}/ood.rs | 26 +- .../{phases => iors}/oracle_handle.rs | 0 src/protocol/{phases => iors}/pesat.rs | 21 +- src/protocol/{phases => iors}/proximity.rs | 28 +- .../{phases => iors}/sample_queries.rs | 22 +- .../{phases => iors}/twin_constraint.rs | 24 +- src/protocol/mod.rs | 2 +- 11 files changed, 379 insertions(+), 212 deletions(-) rename src/protocol/{phases => iors}/batching.rs (96%) rename src/protocol/{phases => iors}/bridge.rs (91%) rename src/protocol/{phases => iors}/mod.rs (66%) rename src/protocol/{phases => iors}/ood.rs (92%) rename src/protocol/{phases => iors}/oracle_handle.rs (100%) rename src/protocol/{phases => iors}/pesat.rs (94%) rename src/protocol/{phases => iors}/proximity.rs (93%) rename src/protocol/{phases => iors}/sample_queries.rs (90%) rename src/protocol/{phases => iors}/twin_constraint.rs (97%) diff --git a/src/lib.rs b/src/lib.rs index 1670530..0a39055 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ pub mod prelude { use crate::crypto::merkle::warp_scheme; use error::{DeciderError, ProverError, VerifierError}; -use protocol::phases::{ +use protocol::iors::{ batching::{ Batching, BatchingProverInputs, BatchingReducedStatement, BatchingReducedWitness, BatchingStatement, BatchingVerifierInputs, @@ -71,7 +71,7 @@ use protocol::phases::{ TwinConstraint, TwinConstraintProverInputs, TwinConstraintReducedStatement, TwinConstraintReducedWitness, TwinConstraintStatement, TwinConstraintWitness, }, - IOR, + IorProveResult, IorVerifyResult, }; pub struct WARP, C: LinearCode + Clone, H: MerkleHasher> { @@ -192,49 +192,52 @@ where } = acc_witness; let acc_fs: Vec> = acc_tds.iter().map(|td| td.codewords()[0].clone()).collect(); - // Phase structs (held by value, borrow setup data for &self lifetime). - let pesat_phase = Pesat:: { + // IOR instances (stateless or borrowing &self.params for the &self lifetime). + let pesat_ior = Pesat:: { code: &self.params.code, hasher: &self.params.hasher, _phantom: PhantomData, }; - let tc_phase = TwinConstraint:: { + let twin_constraint_ior = TwinConstraint:: { r1cs: self.params.p.constraints(), _phantom: PhantomData, }; - let bridge_phase = Bridge::::new(); - let ood_phase = Ood::::new(); - let sample_queries_phase = SampleQueries::::new(); - let batching_phase = Batching::::new(); - let proximity_phase = Proximity:: { + let bridge_ior = Bridge::::new(); + let ood_ior = Ood::::new(); + let sample_queries_ior = SampleQueries::::new(); + let batching_ior = Batching::::new(); + let proximity_ior = Proximity:: { hasher: &self.params.hasher, _phantom: PhantomData, }; - // ── Phase 2: PESAT — encode witnesses, commit, derive τ ────── - let (PesatReducedStatement { mus, taus }, _, PesatReducedWitness { codewords, td_0 }) = - pesat_phase.prove( - prover_state, - &PesatStatement { l1, log_m }, - &PesatWitness { - witnesses: &witnesses, - }, - &(), - )?; - - // ── Phase 3a: TwinConstraint — fused-fold sumcheck ─────────── - let ( - TwinConstraintReducedStatement { + // ── IOR 1: PESAT — encode witnesses, commit, derive τ ──────── + let IorProveResult { + reduced: PesatReducedStatement { mus, taus }, + proof: _, + witness: PesatReducedWitness { codewords, td_0 }, + } = prove_ior!( + pesat_ior, + prover_state, + statement: PesatStatement { l1, log_m }, + witness: PesatWitness { witnesses: &witnesses }, + inputs: (), + )?; + + // ── IOR 2: TwinConstraint — fused-fold sumcheck ────────────── + let IorProveResult { + reduced: TwinConstraintReducedStatement { gamma: _, zeta_0, beta_tau, deferred: _, }, - _, - TwinConstraintReducedWitness { f, z }, - ) = tc_phase.prove( + proof: _, + witness: TwinConstraintReducedWitness { f, z }, + } = prove_ior!( + twin_constraint_ior, prover_state, - &TwinConstraintStatement { + statement: TwinConstraintStatement { acc_instance, l1_mus: mus.clone(), l1_taus: taus, @@ -242,40 +245,37 @@ where log_m, log_n, }, - &TwinConstraintWitness { + witness: TwinConstraintWitness { acc_witness_w: &acc_ws, instances: &instances, witnesses: &witnesses, }, - &TwinConstraintProverInputs { + inputs: TwinConstraintProverInputs { fresh_codewords: &codewords, acc_codewords: &acc_fs, }, )?; - // ── Phase 3b: Bridge — publish (td_new, η, ν₀); split z ────── - let ( - BridgeReducedStatement { + // ── IOR 3: Bridge — publish (td_new, η, ν₀); split z ───────── + let IorProveResult { + reduced: BridgeReducedStatement { eta, nu_0, td_new_root: _, }, - _, - BridgeReducedWitness { - td_new, - new_x, - new_w, - }, - ) = bridge_phase.prove( + proof: _, + witness: BridgeReducedWitness { td_new, new_x, new_w }, + } = prove_ior!( + bridge_ior, prover_state, - &BridgeStatement { + statement: BridgeStatement { zeta_0: zeta_0.clone(), beta_tau: beta_tau.clone(), log_m, n_minus_k: N - k, }, - &BridgeWitness { z: &z, f: &f }, - &BridgeProverInputs { + witness: BridgeWitness { z: &z, f: &f }, + inputs: BridgeProverInputs { bundled_pesat: &self.params.p, hasher: &self.params.hasher, code_len: n, @@ -283,63 +283,72 @@ where }, )?; - // ── Phase 3c: OOD — out-of-domain queries ──────────────────── - let (OodReducedStatement { samples_flat, answers }, _, _) = ood_phase.prove( + // ── IOR 4: OOD — out-of-domain queries on the reduced oracle ── + let IorProveResult { + reduced: OodReducedStatement { samples_flat, answers }, + proof: _, + witness: _, + } = prove_ior!( + ood_ior, prover_state, - &OodStatement { - s: self.params.config.s, - log_n, - }, - &(), - &OodProverInputs { oracle: &f }, + statement: OodStatement { s: self.params.config.s, log_n }, + witness: (), + inputs: OodProverInputs { oracle: &f }, )?; - // ── Phase 3c → 3d: SampleQueries — sample t shift positions ── - let (SampleQueriesReducedStatement { queries }, _, _) = sample_queries_phase.prove( + // ── IOR 5: SampleQueries — sample t shift positions ────────── + let IorProveResult { + reduced: SampleQueriesReducedStatement { queries }, + proof: _, + witness: _, + } = prove_ior!( + sample_queries_ior, prover_state, - &SampleQueriesStatement { + statement: SampleQueriesStatement { log_n, t: self.params.config.t }, + witness: (), + inputs: (), + )?; + + // ── IOR 6: Batching — DAG fan-in over (ζ₀, OOD, queries) ───── + let IorProveResult { + reduced: BatchingReducedStatement { alpha }, + proof: _, + witness: BatchingReducedWitness { mu }, + } = prove_ior!( + batching_ior, + prover_state, + statement: BatchingStatement::from_phase_outputs( + zeta_0.clone(), + &samples_flat, + &queries.evaluation_points, + self.params.config.s, + self.params.config.t, log_n, - t: self.params.config.t, - }, - &(), - &(), + ), + witness: (), + inputs: BatchingProverInputs { oracle: &f }, )?; - // ── Phase 3d: Batching — DAG fan-in over (ζ₀, OOD, queries) ── - let (BatchingReducedStatement { alpha }, _, BatchingReducedWitness { mu }) = batching_phase - .prove( - prover_state, - &BatchingStatement::from_phase_outputs( - zeta_0.clone(), - &samples_flat, - &queries.evaluation_points, - self.params.config.s, - self.params.config.t, - log_n, - ), - &(), - &BatchingProverInputs { oracle: &f }, - )?; - - // ── Phase 3e: Proximity — shift-query openings ─────────────── - let ( - _, - ProximityProofString { + // ── IOR 7: Proximity — open committed oracles at shift positions ── + let IorProveResult { + reduced: _, + proof: ProximityProofString { auth_0, auth_j, shift_query_answers, }, - _, - ) = proximity_phase.prove( + witness: _, + } = prove_ior!( + proximity_ior, prover_state, - &ProximityStatement { + statement: ProximityStatement { queries: queries.clone(), l2, t: self.params.config.t, n, }, - &(), - &ProximityProverInputs { + witness: (), + inputs: ProximityProverInputs { td_0: &td_0, acc_td: &acc_tds, }, @@ -411,46 +420,52 @@ where // statement — Proximity needs them for the per-acc Merkle handles. let l2_roots = parsed_acc.rt.clone(); - // Phase structs (held by value, borrow setup data for &self lifetime). - let pesat_phase = Pesat:: { + // IOR instances. + let pesat_ior = Pesat:: { code: &self.params.code, hasher: &self.params.hasher, _phantom: PhantomData, }; - let tc_phase = TwinConstraint:: { + let twin_constraint_ior = TwinConstraint:: { r1cs: self.params.p.constraints(), _phantom: PhantomData, }; - let bridge_phase = Bridge::::new(); - let ood_phase = Ood::::new(); - let sample_queries_phase = SampleQueries::::new(); - let batching_phase = Batching::::new(); - let proximity_phase = Proximity:: { + let bridge_ior = Bridge::::new(); + let ood_ior = Ood::::new(); + let sample_queries_ior = SampleQueries::::new(); + let batching_ior = Batching::::new(); + let proximity_ior = Proximity:: { hasher: &self.params.hasher, _phantom: PhantomData, }; - // ── Phase 2: PESAT::verify ────────────────────────────────── - let ( - PesatReducedStatement { + // ── IOR 1: PESAT::verify ───────────────────────────────────── + let IorVerifyResult { + reduced: PesatReducedStatement { mus: l1_mus, taus: l1_taus, }, - PesatVerifierOutputs { rt_0 }, - ) = pesat_phase.verify(verifier_state, &PesatStatement { l1, log_m }, &())?; + outputs: PesatVerifierOutputs { rt_0 }, + } = verify_ior!( + pesat_ior, + verifier_state, + statement: PesatStatement { l1, log_m }, + inputs: (), + )?; - // ── Phase 3a: TwinConstraint::verify ──────────────────────── - let ( - TwinConstraintReducedStatement { + // ── IOR 2: TwinConstraint::verify ──────────────────────────── + let IorVerifyResult { + reduced: TwinConstraintReducedStatement { gamma, zeta_0, beta_tau, deferred, }, - _, - ) = tc_phase.verify( + outputs: _, + } = verify_ior!( + twin_constraint_ior, verifier_state, - &TwinConstraintStatement { + statement: TwinConstraintStatement { acc_instance: parsed_acc, l1_mus: l1_mus.clone(), l1_taus: l1_taus.clone(), @@ -458,49 +473,52 @@ where log_m, log_n, }, - &(), + inputs: (), )?; - // ── Phase 3b: Bridge::verify — read (td, η, ν₀), discharge TC's deferred check ─ - let ( - BridgeReducedStatement { + // ── IOR 3: Bridge::verify — read (td, η, ν₀); discharge deferred ─ + let IorVerifyResult { + reduced: BridgeReducedStatement { eta: _, nu_0, td_new_root: _, }, - _, - ) = bridge_phase.verify( + outputs: _, + } = verify_ior!( + bridge_ior, verifier_state, - &BridgeStatement { + statement: BridgeStatement { zeta_0: zeta_0.clone(), beta_tau, log_m, n_minus_k: N - k, }, - &BridgeVerifierInputs { + inputs: BridgeVerifierInputs { deferred: &deferred, gamma: &gamma, }, )?; - // ── Phase 3c: OOD::verify ─────────────────────────────────── - let (OodReducedStatement { samples_flat, answers }, _) = ood_phase.verify( + // ── IOR 4: OOD::verify ─────────────────────────────────────── + let IorVerifyResult { + reduced: OodReducedStatement { samples_flat, answers }, + outputs: _, + } = verify_ior!( + ood_ior, verifier_state, - &OodStatement { - s: self.params.config.s, - log_n, - }, - &(), + statement: OodStatement { s: self.params.config.s, log_n }, + inputs: (), )?; - // ── Phase 3c → 3d: SampleQueries::verify ──────────────────── - let (SampleQueriesReducedStatement { queries }, _) = sample_queries_phase.verify( + // ── IOR 5: SampleQueries::verify ───────────────────────────── + let IorVerifyResult { + reduced: SampleQueriesReducedStatement { queries }, + outputs: _, + } = verify_ior!( + sample_queries_ior, verifier_state, - &SampleQueriesStatement { - log_n, - t: self.params.config.t, - }, - &(), + statement: SampleQueriesStatement { log_n, t: self.params.config.t }, + inputs: (), )?; // ── Whole-proof arity checks (orchestrator-owned) ──────────── @@ -552,23 +570,24 @@ where }) .collect(); - // ── Phase 3e: Proximity::verify ───────────────────────────── - proximity_phase.verify( + // ── IOR 7: Proximity::verify ───────────────────────────────── + verify_ior!( + proximity_ior, verifier_state, - &ProximityStatement { + statement: ProximityStatement { queries: queries.clone(), l2, t: self.params.config.t, n, }, - &ProximityVerifierInputs { + inputs: ProximityVerifierInputs { fresh: &fresh_handle, acc: &acc_handles, _f: PhantomData, }, )?; - // ── Recompute ν vector from shift answers ────────────────── + // ── Recompute ν vector from shift answers (orchestrator-owned) ── let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma); let mut nus = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); nus.push(nu_0); @@ -581,10 +600,14 @@ where nus.push(nu_st); } - // ── Phase 3d: Batching::verify ───────────────────────────── - let (BatchingReducedStatement { alpha }, _) = batching_phase.verify( + // ── IOR 6: Batching::verify ────────────────────────────────── + let IorVerifyResult { + reduced: BatchingReducedStatement { alpha }, + outputs: _, + } = verify_ior!( + batching_ior, verifier_state, - &BatchingStatement::from_phase_outputs( + statement: BatchingStatement::from_phase_outputs( zeta_0.clone(), &samples_flat, &queries.evaluation_points, @@ -592,7 +615,7 @@ where self.params.config.t, log_n, ), - &BatchingVerifierInputs { + inputs: BatchingVerifierInputs { nus, acc_mu: acc_mu_first, }, diff --git a/src/protocol/phases/batching.rs b/src/protocol/iors/batching.rs similarity index 96% rename from src/protocol/phases/batching.rs rename to src/protocol/iors/batching.rs index d0a0a71..69901bc 100644 --- a/src/protocol/phases/batching.rs +++ b/src/protocol/iors/batching.rs @@ -38,7 +38,7 @@ use std::marker::PhantomData; use crate::count_ops; use crate::error::{ProverError, VerifierError}; use crate::protocol::oracle::Oracle; -use crate::protocol::phases::IOR; +use crate::protocol::iors::IOR; use crate::protocol::transcript::EffscVerifierTranscript; use crate::utils::poly::{eq_poly, eq_poly_non_binary}; @@ -176,7 +176,12 @@ impl<'a, F> IOR for Batching<'a, F> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { - type Statement = BatchingStatement; + const NAME: &'static str = "Batching"; + + type Statement<'b> + = BatchingStatement + where + Self: 'b; type Witness<'b> = () where @@ -195,11 +200,14 @@ where type ReducedWitness = BatchingReducedWitness; type VerifierOutputs = (); - fn reduce_statement( + fn reduce_statement<'b>( &self, - _statement: &Self::Statement, + _statement: &Self::Statement<'b>, inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement { + ) -> Self::ReducedStatement + where + Self: 'b, + { BatchingReducedStatement { alpha: inputs.alpha.clone(), } @@ -213,7 +221,7 @@ where fn prove_inner<'b>( &self, prover_state: &mut ProverState, - statement: &Self::Statement, + statement: &Self::Statement<'b>, _witness: &Self::Witness<'b>, inputs: &Self::ProverInputs<'b>, ) -> Result< @@ -225,6 +233,7 @@ where ProverError, > where + Self: 'b, 'a: 'b, { let n = inputs.oracle.len(); @@ -286,10 +295,11 @@ where fn verify_inner<'b, 'c>( &self, verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement, + statement: &Self::Statement<'c>, inputs: &Self::VerifierInputs<'c>, ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> where + Self: 'c, 'a: 'c, { let r = 1 + statement.s + statement.t; diff --git a/src/protocol/phases/bridge.rs b/src/protocol/iors/bridge.rs similarity index 91% rename from src/protocol/phases/bridge.rs rename to src/protocol/iors/bridge.rs index 07c5d05..1a85cfb 100644 --- a/src/protocol/phases/bridge.rs +++ b/src/protocol/iors/bridge.rs @@ -46,8 +46,8 @@ use crate::count_ops; use crate::crypto::merkle::{warp_scheme, WarpCommitted}; use crate::error::{ProverError, VerifierError}; use crate::protocol::oracle::Oracle; -use crate::protocol::phases::twin_constraint::DeferredOracleCheck; -use crate::protocol::phases::IOR; +use crate::protocol::iors::twin_constraint::DeferredOracleCheck; +use crate::protocol::iors::IOR; use crate::relations::BundledPESAT; pub struct BridgeStatement { @@ -137,14 +137,19 @@ where } } -impl<'a, F, P, H> IOR for Bridge<'a, F, P, H> +impl<'cfg, F, P, H> IOR for Bridge<'cfg, F, P, H> where F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, P: BundledPESAT, H: MerkleHasher>, H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize + Clone, { - type Statement = BridgeStatement; + const NAME: &'static str = "Bridge"; + + type Statement<'b> + = BridgeStatement + where + Self: 'b; type Witness<'b> = BridgeWitness<'b, F> where @@ -163,11 +168,14 @@ where type ReducedWitness = BridgeReducedWitness; type VerifierOutputs = (); - fn reduce_statement( + fn reduce_statement<'a>( &self, - _statement: &Self::Statement, + _statement: &Self::Statement<'a>, inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement { + ) -> Self::ReducedStatement + where + Self: 'a, + { BridgeReducedStatement { eta: inputs.eta, nu_0: inputs.nu_0, @@ -180,12 +188,12 @@ where skip_all, fields(log_m = statement.log_m, n_minus_k = statement.n_minus_k) )] - fn prove_inner<'b>( + fn prove_inner<'a>( &self, prover_state: &mut ProverState, - statement: &Self::Statement, - witness: &Self::Witness<'b>, - inputs: &Self::ProverInputs<'b>, + statement: &Self::Statement<'a>, + witness: &Self::Witness<'a>, + inputs: &Self::ProverInputs<'a>, ) -> Result< ( Self::ReductionInputs, @@ -195,7 +203,7 @@ where ProverError, > where - Self: 'b, + Self: 'a, { // η = ⟨ eq(β_τ), p(z) ⟩ let beta_eq_evals = compute_hypercube_eq_evals(statement.log_m, &statement.beta_tau); @@ -242,14 +250,14 @@ where } #[tracing::instrument(name = "bridge.verify", skip_all)] - fn verify_inner<'b, 'c>( + fn verify_inner<'a, 'b>( &self, - verifier_state: &mut VerifierState<'b>, - _statement: &Self::Statement, - inputs: &Self::VerifierInputs<'c>, + verifier_state: &mut VerifierState<'a>, + _statement: &Self::Statement<'b>, + inputs: &Self::VerifierInputs<'b>, ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> where - Self: 'c, + Self: 'b, { // Read (td_digest, η, ν₀) from the transcript. let td_new_root: H::Digest = verifier_state.prover_message()?; diff --git a/src/protocol/phases/mod.rs b/src/protocol/iors/mod.rs similarity index 66% rename from src/protocol/phases/mod.rs rename to src/protocol/iors/mod.rs index 2680e4e..57a6176 100644 --- a/src/protocol/phases/mod.rs +++ b/src/protocol/iors/mod.rs @@ -60,17 +60,19 @@ use crate::error::{ProverError, VerifierError}; /// where prover and verifier compute the same reduction via parallel /// code paths that silently diverge. pub trait IOR { - /// Pre-reduction claim. Visible to prover and verifier. - type Statement; - /// Prover-only data the prover reads. Lifetime-parameterized so a - /// pipeline composition can borrow inter-phase data into the witness - /// for one `prove` call without the borrow leaking into the phase - /// struct's lifetime — see `crate::protocol::composition`. + /// Human-readable identifier — the WARP paper's name for the IOR. + const NAME: &'static str; + + /// Pre-reduction claim. Visible to prover and verifier. GAT so the + /// statement may borrow from upstream-IOR outputs. + type Statement<'a> + where + Self: 'a; + /// Prover-only data the prover reads. type Witness<'a> where Self: 'a; /// Oracles flowing in from upstream IORs (prover view: full data). - /// Lifetime-parameterized for the same reason as `Witness`. type ProverInputs<'a> where Self: 'a; @@ -96,7 +98,7 @@ pub trait IOR { /// the verifier; the trait does not assume it is private. type ReducedWitness; /// Verifier-side outputs (commitments, parsed digests). Threaded into - /// downstream phase `VerifierInputs`. + /// downstream IORs' `VerifierInputs`. type VerifierOutputs; /// THE single source of truth for `ReducedStatement`. @@ -105,11 +107,13 @@ pub trait IOR { /// [`Self::ReductionInputs`] (assembled in `prove_inner` / /// `verify_inner`). Drift between sides is structurally impossible /// because both go through this function. - fn reduce_statement( + fn reduce_statement<'a>( &self, - statement: &Self::Statement, + statement: &Self::Statement<'a>, inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement; + ) -> Self::ReducedStatement + where + Self: 'a; /// Implementor's prover-side body. Runs the protocol's prover /// machinery and returns the inputs `reduce_statement` needs, plus @@ -117,7 +121,7 @@ pub trait IOR { fn prove_inner<'a>( &self, prover_state: &mut ProverState, - statement: &Self::Statement, + statement: &Self::Statement<'a>, witness: &Self::Witness<'a>, inputs: &Self::ProverInputs<'a>, ) -> Result< @@ -137,7 +141,7 @@ pub trait IOR { fn verify_inner<'a, 'b>( &self, verifier_state: &mut VerifierState<'a>, - statement: &Self::Statement, + statement: &Self::Statement<'b>, inputs: &Self::VerifierInputs<'b>, ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> where @@ -147,7 +151,7 @@ pub trait IOR { fn prove<'a>( &self, prover_state: &mut ProverState, - statement: &Self::Statement, + statement: &Self::Statement<'a>, witness: &Self::Witness<'a>, inputs: &Self::ProverInputs<'a>, ) -> Result< @@ -171,7 +175,7 @@ pub trait IOR { fn verify<'a, 'b>( &self, verifier_state: &mut VerifierState<'a>, - statement: &Self::Statement, + statement: &Self::Statement<'b>, inputs: &Self::VerifierInputs<'b>, ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> where @@ -182,3 +186,82 @@ pub trait IOR { Ok((reduced, vouts)) } } + +// ─── Choreography-syntax helpers ───────────────────────────────────────── +// +// These wrap the trait's tuple return into named-field structs so +// `lib.rs` can destructure `reduced` / `proof` / `witness` by name. The +// IOR trait itself stays formal (Statement / Witness / ProverInputs / +// ReductionInputs / ReducedStatement / ProofString / ReducedWitness / +// VerifierOutputs); these are pure call-site syntax sugar. + +/// Generic destructuring carrier for `IOR::prove`. The macro +/// [`prove_ior!`] returns this so callers can write +/// `let IorProveResult { reduced, proof, witness } = prove_ior!(...)?;`. +pub struct IorProveResult { + pub reduced: R, + pub proof: P, + pub witness: W, +} + +/// Generic destructuring carrier for `IOR::verify`. +pub struct IorVerifyResult { + pub reduced: R, + pub outputs: V, +} + +/// Calls `IOR::prove` on the given IOR and packages the `(reduced, +/// proof, witness)` tuple into a named-field [`IorProveResult`]. +/// +/// ```ignore +/// let IorProveResult { +/// reduced: PesatReducedStatement { mus, taus }, +/// proof: _, +/// witness: PesatReducedWitness { codewords, td_0 }, +/// } = prove_ior!( +/// pesat_ior, +/// prover_state, +/// statement: PesatStatement { l1, log_m }, +/// witness: PesatWitness { witnesses: &witnesses }, +/// inputs: (), +/// )?; +/// ``` +#[macro_export] +macro_rules! prove_ior { + ( + $ior:expr, + $transcript:expr, + statement: $statement:expr, + witness: $witness:expr, + inputs: $inputs:expr $(,)? + ) => {{ + let __stmt = $statement; + let __wit = $witness; + let __ins = $inputs; + $crate::protocol::iors::IOR::prove(&$ior, $transcript, &__stmt, &__wit, &__ins).map( + |(reduced, proof, witness)| $crate::protocol::iors::IorProveResult { + reduced, + proof, + witness, + }, + ) + }}; +} + +/// Calls `IOR::verify` on the given IOR and packages the `(reduced, +/// outputs)` tuple into a named-field [`IorVerifyResult`]. +#[macro_export] +macro_rules! verify_ior { + ( + $ior:expr, + $transcript:expr, + statement: $statement:expr, + inputs: $inputs:expr $(,)? + ) => {{ + let __stmt = $statement; + let __ins = $inputs; + $crate::protocol::iors::IOR::verify(&$ior, $transcript, &__stmt, &__ins).map( + |(reduced, outputs)| $crate::protocol::iors::IorVerifyResult { reduced, outputs }, + ) + }}; +} diff --git a/src/protocol/phases/ood.rs b/src/protocol/iors/ood.rs similarity index 92% rename from src/protocol/phases/ood.rs rename to src/protocol/iors/ood.rs index a799357..d1d1689 100644 --- a/src/protocol/phases/ood.rs +++ b/src/protocol/iors/ood.rs @@ -26,7 +26,7 @@ use std::marker::PhantomData; use crate::count_ops; use crate::error::{ProverError, VerifierError}; use crate::protocol::oracle::Oracle; -use crate::protocol::phases::IOR; +use crate::protocol::iors::IOR; pub struct OodStatement { pub s: usize, @@ -73,7 +73,12 @@ impl<'a, F> IOR for Ood<'a, F> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { - type Statement = OodStatement; + const NAME: &'static str = "OOD"; + + type Statement<'b> + = OodStatement + where + Self: 'b; type Witness<'b> = () where @@ -92,11 +97,14 @@ where type ReducedWitness = (); type VerifierOutputs = (); - fn reduce_statement( + fn reduce_statement<'b>( &self, - _statement: &Self::Statement, + _statement: &Self::Statement<'b>, inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement { + ) -> Self::ReducedStatement + where + Self: 'b, + { OodReducedStatement { samples_flat: inputs.samples_flat.clone(), answers: inputs.answers.clone(), @@ -107,7 +115,7 @@ where fn prove_inner<'b>( &self, prover_state: &mut ProverState, - statement: &Self::Statement, + statement: &Self::Statement<'b>, _witness: &Self::Witness<'b>, inputs: &Self::ProverInputs<'b>, ) -> Result< @@ -119,7 +127,7 @@ where ProverError, > where - 'a: 'b, + Self: 'b, { let samples_flat = prover_state.verifier_messages_vec::(statement.s * statement.log_n); count_ops!(OodPointQueries, statement.s as u64); @@ -146,11 +154,11 @@ where fn verify_inner<'b, 'c>( &self, verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement, + statement: &Self::Statement<'c>, _inputs: &Self::VerifierInputs<'c>, ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> where - 'a: 'c, + Self: 'c, { let samples_flat: Vec = (0..statement.s * statement.log_n) .map(|_| verifier_state.verifier_message::()) diff --git a/src/protocol/phases/oracle_handle.rs b/src/protocol/iors/oracle_handle.rs similarity index 100% rename from src/protocol/phases/oracle_handle.rs rename to src/protocol/iors/oracle_handle.rs diff --git a/src/protocol/phases/pesat.rs b/src/protocol/iors/pesat.rs similarity index 94% rename from src/protocol/phases/pesat.rs rename to src/protocol/iors/pesat.rs index 8504832..c4c5049 100644 --- a/src/protocol/phases/pesat.rs +++ b/src/protocol/iors/pesat.rs @@ -26,7 +26,7 @@ use std::marker::PhantomData; use crate::count_ops; use crate::crypto::merkle::{encode_codewords, warp_scheme, WarpCommitted}; use crate::error::{ProverError, VerifierError}; -use crate::protocol::phases::IOR; +use crate::protocol::iors::IOR; pub struct PesatStatement { pub l1: usize, @@ -79,7 +79,11 @@ where H: MerkleHasher>, H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { - type Statement = PesatStatement; + const NAME: &'static str = "PESAT"; + type Statement<'b> + = PesatStatement + where + Self: 'b; type Witness<'b> = PesatWitness<'b, F> where @@ -98,11 +102,14 @@ where type ReducedWitness = PesatReducedWitness; type VerifierOutputs = PesatVerifierOutputs; - fn reduce_statement( + fn reduce_statement<'b>( &self, - _statement: &Self::Statement, + _statement: &Self::Statement<'b>, inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement { + ) -> Self::ReducedStatement + where + Self: 'b, + { PesatReducedStatement { mus: inputs.mus.clone(), taus: inputs.taus.clone(), @@ -117,7 +124,7 @@ where fn prove_inner<'b>( &self, prover_state: &mut ProverState, - statement: &Self::Statement, + statement: &Self::Statement<'b>, witness: &Self::Witness<'b>, _inputs: &Self::ProverInputs<'b>, ) -> Result< @@ -179,7 +186,7 @@ where fn verify_inner<'b, 'c>( &self, verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement, + statement: &Self::Statement<'c>, _inputs: &Self::VerifierInputs<'c>, ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> where diff --git a/src/protocol/phases/proximity.rs b/src/protocol/iors/proximity.rs similarity index 93% rename from src/protocol/phases/proximity.rs rename to src/protocol/iors/proximity.rs index 5ce6eaf..349846d 100644 --- a/src/protocol/phases/proximity.rs +++ b/src/protocol/iors/proximity.rs @@ -26,8 +26,8 @@ use std::marker::PhantomData; use crate::count_ops; use crate::crypto::merkle::{warp_scheme, WarpCommitted, WarpProof}; use crate::error::{ProverError, VerifierError}; -use crate::protocol::phases::oracle_handle::IndexedOracle; -use crate::protocol::phases::IOR; +use crate::protocol::iors::oracle_handle::IndexedOracle; +use crate::protocol::iors::IOR; use crate::protocol::query::QueryIndices; pub struct ProximityStatement { @@ -100,7 +100,12 @@ where F: Field, H: MerkleHasher>, { - type Statement = ProximityStatement; + const NAME: &'static str = "Proximity"; + + type Statement<'b> + = ProximityStatement + where + Self: 'b; type Witness<'b> = () where @@ -113,7 +118,7 @@ where = ProximityVerifierInputs< 'b, F, - crate::protocol::phases::oracle_handle::MerkleIndexedOracle<'b, F, H>, + crate::protocol::iors::oracle_handle::MerkleIndexedOracle<'b, F, H>, > where Self: 'b; @@ -123,11 +128,14 @@ where type ReducedWitness = (); type VerifierOutputs = (); - fn reduce_statement( + fn reduce_statement<'b>( &self, - _statement: &Self::Statement, + _statement: &Self::Statement<'b>, _inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement { + ) -> Self::ReducedStatement + where + Self: 'b, + { } #[tracing::instrument( @@ -141,7 +149,7 @@ where fn prove_inner<'b>( &self, _prover_state: &mut ProverState, - statement: &Self::Statement, + statement: &Self::Statement<'b>, _witness: &Self::Witness<'b>, inputs: &Self::ProverInputs<'b>, ) -> Result< @@ -153,6 +161,7 @@ where ProverError, > where + Self: 'b, 'a: 'b, H: 'b, { @@ -233,10 +242,11 @@ where fn verify_inner<'b, 'c>( &self, _verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement, + statement: &Self::Statement<'c>, inputs: &Self::VerifierInputs<'c>, ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> where + Self: 'c, 'a: 'c, H: 'c, { diff --git a/src/protocol/phases/sample_queries.rs b/src/protocol/iors/sample_queries.rs similarity index 90% rename from src/protocol/phases/sample_queries.rs rename to src/protocol/iors/sample_queries.rs index 29d04d5..b2a23c8 100644 --- a/src/protocol/phases/sample_queries.rs +++ b/src/protocol/iors/sample_queries.rs @@ -25,7 +25,7 @@ use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState use std::marker::PhantomData; use crate::error::{ProverError, VerifierError}; -use crate::protocol::phases::IOR; +use crate::protocol::iors::IOR; use crate::protocol::query::QueryIndices; pub struct SampleQueriesStatement { @@ -64,7 +64,12 @@ impl<'a, F> IOR for SampleQueries<'a, F> where F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { - type Statement = SampleQueriesStatement; + const NAME: &'static str = "SampleQueries"; + + type Statement<'b> + = SampleQueriesStatement + where + Self: 'b; type Witness<'b> = () where @@ -83,11 +88,14 @@ where type ReducedWitness = (); type VerifierOutputs = (); - fn reduce_statement( + fn reduce_statement<'b>( &self, - _statement: &Self::Statement, + _statement: &Self::Statement<'b>, inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement { + ) -> Self::ReducedStatement + where + Self: 'b, + { SampleQueriesReducedStatement { queries: inputs.queries.clone(), } @@ -97,7 +105,7 @@ where fn prove_inner<'b>( &self, prover_state: &mut ProverState, - statement: &Self::Statement, + statement: &Self::Statement<'b>, _witness: &Self::Witness<'b>, _inputs: &Self::ProverInputs<'b>, ) -> Result< @@ -119,7 +127,7 @@ where fn verify_inner<'b, 'c>( &self, verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement, + statement: &Self::Statement<'c>, _inputs: &Self::VerifierInputs<'c>, ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> where diff --git a/src/protocol/phases/twin_constraint.rs b/src/protocol/iors/twin_constraint.rs similarity index 97% rename from src/protocol/phases/twin_constraint.rs rename to src/protocol/iors/twin_constraint.rs index 2bad4a2..2f137cd 100644 --- a/src/protocol/phases/twin_constraint.rs +++ b/src/protocol/iors/twin_constraint.rs @@ -49,7 +49,7 @@ use std::marker::PhantomData; use crate::count_ops; use crate::error::{ProverError, VerifierError}; use crate::protocol::oracle::Oracle; -use crate::protocol::phases::IOR; +use crate::protocol::iors::IOR; use crate::protocol::transcript::EffscVerifierTranscript; use crate::relations::r1cs::R1CSConstraints; use crate::types::AccumulatorInstance; @@ -288,7 +288,12 @@ where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, H: MerkleHasher, { - type Statement = TwinConstraintStatement; + const NAME: &'static str = "TwinConstraint"; + + type Statement<'b> + = TwinConstraintStatement + where + Self: 'b; type Witness<'b> = TwinConstraintWitness<'b, F> where @@ -310,11 +315,14 @@ where /// Single source of truth for ζ₀ / β_τ. Both prover and verifier /// land here with `(ω, τ, γ, final_claim)`; the new accumulator /// state is computed identically on both sides. - fn reduce_statement( + fn reduce_statement<'b>( &self, - statement: &Self::Statement, + statement: &Self::Statement<'b>, inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement { + ) -> Self::ReducedStatement + where + Self: 'b, + { let log_l = statement.log_l; let log_n = statement.log_n; let l1 = statement.l1_mus.len(); @@ -359,7 +367,7 @@ where fn prove_inner<'b>( &self, prover_state: &mut ProverState, - statement: &Self::Statement, + statement: &Self::Statement<'b>, witness: &Self::Witness<'b>, inputs: &Self::ProverInputs<'b>, ) -> Result< @@ -371,6 +379,7 @@ where ProverError, > where + Self: 'b, 'a: 'b, H: 'b, { @@ -469,10 +478,11 @@ where fn verify_inner<'b, 'c>( &self, verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement, + statement: &Self::Statement<'c>, _inputs: &Self::VerifierInputs<'c>, ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> where + Self: 'c, 'a: 'c, H: 'c, { diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 55ed4d7..9d93709 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,4 +1,4 @@ pub mod oracle; -pub mod phases; +pub mod iors; pub mod query; pub mod transcript; From 366a01dac18a42d8145016a9aa4046b3cd72f788 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 12 May 2026 15:05:47 +0200 Subject: [PATCH 56/68] cleanup: drop dead types, prune verbose comments, drop "phase" vocab Removes AccumulatorEntry, AccumulatorWitnessEntry, push_entry/from_entries, and PesatOutput (all orphaned from the pre-IOR refactor). Renames from_phase_outputs -> from_ior_outputs. Cuts the long narrative blocks in iors/mod.rs and aligns module docstrings on "IOR" rather than "phase". --- src/crypto/merkle/mod.rs | 9 ++- src/lib.rs | 32 ++++------ src/profile/mod.rs | 2 +- src/protocol/iors/batching.rs | 6 +- src/protocol/iors/bridge.rs | 4 +- src/protocol/iors/mod.rs | 87 ++++------------------------ src/protocol/iors/ood.rs | 6 +- src/protocol/iors/oracle_handle.rs | 22 ++++--- src/protocol/iors/pesat.rs | 11 ++-- src/protocol/iors/proximity.rs | 20 +++---- src/protocol/iors/sample_queries.rs | 4 +- src/protocol/iors/twin_constraint.rs | 8 +-- src/protocol/oracle.rs | 16 ++--- src/types.rs | 80 +------------------------ 14 files changed, 72 insertions(+), 235 deletions(-) diff --git a/src/crypto/merkle/mod.rs b/src/crypto/merkle/mod.rs index e7e0a7f..339aeef 100644 --- a/src/crypto/merkle/mod.rs +++ b/src/crypto/merkle/mod.rs @@ -1,10 +1,9 @@ //! Warp's vector-commitment layer, built on `ark-vc` / `ark-mt`. //! -//! Replaces the previous `ark-crypto-primitives::merkle_tree`-based -//! implementation. Warp commits to L1 codewords interleaved into one -//! Merkle tree (PESAT phase) and to single folded codewords across the -//! accumulator. Both shapes are handled by `MultiVectorMerkleCommitment` -//! (the m=1 case is a degenerate single-codeword tree). +//! Warp commits to L1 codewords interleaved into one Merkle tree (PESAT) +//! and to single folded codewords across the accumulator. Both shapes +//! are handled by `MultiVectorMerkleCommitment` (the m=1 case is a +//! degenerate single-codeword tree). use ark_codes::traits::LinearCode; use ark_ff::Field; diff --git a/src/lib.rs b/src/lib.rs index 0a39055..f17c774 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,8 +39,7 @@ pub mod prelude { pub use crate::error::{DeciderError, ProverError, VerifierError, WARPError}; pub use crate::traits::AccumulationScheme; pub use crate::types::{ - AccumulatorEntry, AccumulatorInstance, AccumulatorWitness, AccumulatorWitnessEntry, - WARPProof, WARPProverKey, WARPVerifierKey, + AccumulatorInstance, AccumulatorWitness, WARPProof, WARPProverKey, WARPVerifierKey, }; pub use crate::WARP; } @@ -183,16 +182,14 @@ where absorb_instances(prover_state, &instances); acc_instance.absorb_into(prover_state); - // Destructure acc_witness. Codewords live inside td[i].codewords(); - // we extract the single codeword per accumulator entry into acc_fs - // for use by twin_constraint. + // Extract the single codeword per accumulator entry for twin_constraint. let AccumulatorWitness { td: acc_tds, w: acc_ws, } = acc_witness; let acc_fs: Vec> = acc_tds.iter().map(|td| td.codewords()[0].clone()).collect(); - // IOR instances (stateless or borrowing &self.params for the &self lifetime). + // IOR instances. let pesat_ior = Pesat:: { code: &self.params.code, hasher: &self.params.hasher, @@ -317,7 +314,7 @@ where } = prove_ior!( batching_ior, prover_state, - statement: BatchingStatement::from_phase_outputs( + statement: BatchingStatement::from_ior_outputs( zeta_0.clone(), &samples_flat, &queries.evaluation_points, @@ -400,15 +397,11 @@ where let log_n = log2(n) as usize; // Parse the transcript-side statement (l1 fresh instances + l2 - // accumulated entries). Stays in the orchestrator: it crosses - // the pipeline/non-pipeline boundary by definition. + // accumulated entries). let (l1_xs, parsed_acc) = parse_statement::(verifier_state, l1, l2, N - k, log_n, log_m)?; - // Cache prior-accumulator values needed by the post-pipeline - // (α, β) consistency checks. These cross-reference the prior - // single-entry `acc_instance` against the pipeline's reduced - // outputs, so they live outside the pipeline. + // Cache prior-accumulator values for the post-IOR (α, β) consistency checks. let acc_alpha_first = acc_instance.alpha[0].clone(); let acc_beta_0_first = acc_instance.beta.0[0].clone(); let acc_beta_1_first = acc_instance.beta.1[0].clone(); @@ -521,7 +514,7 @@ where inputs: (), )?; - // ── Whole-proof arity checks (orchestrator-owned) ──────────── + // ── Whole-proof arity checks ───────────────────────────────── (proof.shift_query_answers.len() == self.params.config.t) .then_some(()) .ok_or(VerifierError::NumShiftQueries)?; @@ -587,7 +580,7 @@ where }, )?; - // ── Recompute ν vector from shift answers (orchestrator-owned) ── + // ── Recompute ν vector from shift answers ─────────────────── let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma); let mut nus = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); nus.push(nu_0); @@ -607,7 +600,7 @@ where } = verify_ior!( batching_ior, verifier_state, - statement: BatchingStatement::from_phase_outputs( + statement: BatchingStatement::from_ior_outputs( zeta_0.clone(), &samples_flat, &queries.evaluation_points, @@ -621,7 +614,7 @@ where }, )?; - // ── Post-pipeline accumulator consistency ────────────────── + // ── Post-IOR accumulator consistency ─────────────────────── (acc_alpha_first == alpha) .then_some(()) .ok_or(VerifierError::CodeEvaluationPoint)?; @@ -646,20 +639,16 @@ where acc_witness: AccumulatorWitness, acc_instance: AccumulatorInstance, ) -> Result<(), WARPError> { - // The accumulator witness's codeword is td[0].codewords()[0]. let acc_codeword = &acc_witness.td[0].codewords()[0]; - // Re-encode the witness; check codeword match. let computed_f = self.params.code.encode(&acc_witness.w[0]); (acc_codeword == &computed_f).then_some(()).ok_or(DeciderError::EncodedWitness)?; - // Re-commit and check root. let scheme = warp_scheme::(self.params.hasher.clone(), self.params.code.code_len()); let recomputed = scheme.commit(std::slice::from_ref(&computed_f)); (acc_instance.rt[0] == *recomputed.root()).then_some(()).ok_or(DeciderError::MerkleRoot)?; (acc_witness.td[0].root() == recomputed.root()).then_some(()).ok_or(DeciderError::MerkleTrapDoor)?; - // MLE evaluation check. let f_hat = DenseMultilinearExtension::from_evaluations_slice( log2(self.params.code.code_len()) as usize, acc_codeword, @@ -667,7 +656,6 @@ where (f_hat.evaluate(&acc_instance.alpha[0]) == acc_instance.mu[0]) .then_some(()).ok_or(DeciderError::MLExtensionEvaluation)?; - // Bundled-evaluation check. let tau = &acc_instance.beta.0[0]; let tau_zero_evader = compute_hypercube_eq_evals(tau.len(), tau); let mut z = acc_instance.beta.1[0].clone(); diff --git a/src/profile/mod.rs b/src/profile/mod.rs index 575ceaf..351a213 100644 --- a/src/profile/mod.rs +++ b/src/profile/mod.rs @@ -1,6 +1,6 @@ //! Profile instrumentation. //! -//! The library always emits `tracing` spans at phase boundaries; this +//! The library always emits `tracing` spans at IOR boundaries; this //! module installs a subscriber that renders those spans. Rendering is //! off by default — the `profile` cargo feature is required to bring in //! `tracing-subscriber` and `libc` (for `clock_gettime` / `getrusage`). diff --git a/src/protocol/iors/batching.rs b/src/protocol/iors/batching.rs index 69901bc..c29810f 100644 --- a/src/protocol/iors/batching.rs +++ b/src/protocol/iors/batching.rs @@ -1,4 +1,4 @@ -//! Batching sumcheck phase. +//! Batching sumcheck IOR. //! //! Paired spec: `docs/paper-mods/mod1_oracle.tex` (oracle composition). //! Reduces the batched claim @@ -101,7 +101,7 @@ impl BatchingStatement { /// through this. Drift between sides becomes structurally impossible. /// /// Layout: `[ζ_0, ood_chunk_0, …, ood_chunk_{s-1}, query_0, …, query_{t-1}]`. - pub fn from_phase_outputs( + pub fn from_ior_outputs( zeta_0: Vec, ood_samples_flat: &[F], query_eval_points: &[Vec], @@ -153,7 +153,7 @@ pub struct BatchingReducedWitness { pub mu: F, } -/// Batching phase configuration. +/// Batching IOR configuration. pub struct Batching<'a, F: Field> { pub _phantom: PhantomData<&'a F>, } diff --git a/src/protocol/iors/bridge.rs b/src/protocol/iors/bridge.rs index 1a85cfb..bda2536 100644 --- a/src/protocol/iors/bridge.rs +++ b/src/protocol/iors/bridge.rs @@ -1,4 +1,4 @@ -//! TC → OOD bridge phase. +//! TC → OOD bridge IOR. //! //! After TwinConstraint reduces to `(γ, ζ₀, β_τ)` on the verifier side //! and `(γ, ζ₀, β_τ, deferred, f, z)` on the prover side, three new @@ -101,7 +101,7 @@ where pub new_w: Vec, } -/// TC → OOD bridge phase configuration. Stateless; the lifetime +/// TC → OOD bridge IOR configuration. Stateless; the lifetime /// parameter exists only to anchor `ProverInputs<'a>` / `Witness<'a>` /// for the IOR trait impl. pub struct Bridge<'a, F, P, H> diff --git a/src/protocol/iors/mod.rs b/src/protocol/iors/mod.rs index 57a6176..00a1f1b 100644 --- a/src/protocol/iors/mod.rs +++ b/src/protocol/iors/mod.rs @@ -1,29 +1,6 @@ -//! Warp IOR phases as first-class modules. -//! -//! Paired spec: `docs/paper-mods/mod1_oracle.tex` (composition rule) and the -//! forthcoming `docs/paper-mods/mod3_accumulator_state.tex`. -//! -//! Each submodule implements one Interactive Oracle Reduction from the Warp -//! construction. The [`IOR`] trait below names the paper-level components -//! and splits oracle types into prover-side / verifier-side halves so the -//! trait can serve both roles against the same struct. -//! -//! Implementor pattern: a phase is a struct holding setup parameters -//! (codes, hashers); implementors write [`IOR::prove_inner`], -//! [`IOR::verify_inner`], and [`IOR::reduce_statement`]. The trait provides -//! default `prove` / `verify` that mechanically chain -//! `*_inner -> reduce_statement` so the prover and verifier *cannot* drift -//! on how `ReducedStatement` is computed from transcript data. -//! -//! The top-level orchestrators in `src/lib.rs::WARP::prove` and `::verify` -//! thread state between phases by chaining `IOR::prove` / `IOR::verify` -//! calls — each phase's `ReducedStatement`, `ProofString`, and -//! `ReducedWitness` feed the next phase's `Statement` / `ProverInputs` / -//! the global proof object. +//! Warp's Interactive Oracle Reductions. Paired spec: +//! `docs/paper-mods/mod1_oracle.tex`. -// `prove_inner` returns the 3-tuple `(ReductionInputs, ProofString, -// ReducedWitness)`; clippy flags the resulting type as "complex" but -// the structure is exactly what the IOR formalism asks for. #![allow(clippy::type_complexity)] pub mod batching; @@ -39,74 +16,33 @@ use spongefish::{ProverState, VerifierState}; use crate::error::{ProverError, VerifierError}; -/// Interactive Oracle Reduction. -/// -/// Mirrors the IOR signature from `docs/paper-mods/mod1_oracle.tex` §4: -/// -/// ```text -/// (stmt, wit, oracles_in) --> (stmt', oracles_out) -/// ``` -/// -/// **Convention.** `Witness` and `ProverInputs` are conventionally -/// reference-holding wrappers (e.g. `PesatWitness<'a, F> { witnesses: -/// &'a [Vec] }`). The trait takes them by `&` so the caller never has -/// to clone bulk data on the way in. None of WARP / WHIR / STIR / FRI / -/// sumcheck-based protocols consume their witness at the IOR level. -/// -/// **Drift prevention.** Implementors write [`prove_inner`], -/// [`verify_inner`], and [`reduce_statement`]. The default `prove` / -/// `verify` chain them automatically; the `ReducedStatement` is computed -/// in exactly one place (`reduce_statement`), eliminating the bug class -/// where prover and verifier compute the same reduction via parallel -/// code paths that silently diverge. +/// Interactive Oracle Reduction. `(stmt, wit, oracles_in) -> (stmt', +/// oracles_out)`. Implementors write `prove_inner` / `verify_inner` / +/// `reduce_statement`; the default `prove` / `verify` chain them so the +/// reduced statement is computed in one place and prover/verifier +/// cannot drift. pub trait IOR { - /// Human-readable identifier — the WARP paper's name for the IOR. const NAME: &'static str; - /// Pre-reduction claim. Visible to prover and verifier. GAT so the - /// statement may borrow from upstream-IOR outputs. type Statement<'a> where Self: 'a; - /// Prover-only data the prover reads. type Witness<'a> where Self: 'a; - /// Oracles flowing in from upstream IORs (prover view: full data). type ProverInputs<'a> where Self: 'a; - /// Oracles flowing in from upstream IORs (verifier view: commitments). type VerifierInputs<'a> where Self: 'a; - /// Explicit inputs that determine [`Self::ReducedStatement`]. Both - /// prover-side and verifier-side machinery produce this struct, then - /// hand it to [`Self::reduce_statement`] for the (single) statement - /// derivation. type ReductionInputs; - /// Post-reduction claim. Visible to prover and verifier. Computed - /// exclusively by [`Self::reduce_statement`]. type ReducedStatement; - /// Out-of-band proof bytes the verifier always reads (auth paths, - /// shift-query answers, …). The orchestrator collects these into the - /// global proof object. type ProofString; - /// Reduced-witness handoff to the next IOR — full prover-side data - /// (oracles, witness vectors, committed trees). In terminal-round - /// protocols (e.g. WHIR's final fold) this can also be revealed to - /// the verifier; the trait does not assume it is private. type ReducedWitness; - /// Verifier-side outputs (commitments, parsed digests). Threaded into - /// downstream IORs' `VerifierInputs`. type VerifierOutputs; - /// THE single source of truth for `ReducedStatement`. - /// - /// Both prover and verifier call this with their respective - /// [`Self::ReductionInputs`] (assembled in `prove_inner` / - /// `verify_inner`). Drift between sides is structurally impossible - /// because both go through this function. + /// Single source of truth: both sides feed their `ReductionInputs` here. fn reduce_statement<'a>( &self, statement: &Self::Statement<'a>, @@ -189,11 +125,8 @@ pub trait IOR { // ─── Choreography-syntax helpers ───────────────────────────────────────── // -// These wrap the trait's tuple return into named-field structs so -// `lib.rs` can destructure `reduced` / `proof` / `witness` by name. The -// IOR trait itself stays formal (Statement / Witness / ProverInputs / -// ReductionInputs / ReducedStatement / ProofString / ReducedWitness / -// VerifierOutputs); these are pure call-site syntax sugar. +// Call-site sugar: the macros wrap the trait's tuple return into the +// named-field structs below so `lib.rs` can destructure by name. /// Generic destructuring carrier for `IOR::prove`. The macro /// [`prove_ior!`] returns this so callers can write diff --git a/src/protocol/iors/ood.rs b/src/protocol/iors/ood.rs index d1d1689..a45466b 100644 --- a/src/protocol/iors/ood.rs +++ b/src/protocol/iors/ood.rs @@ -1,6 +1,6 @@ -//! Out-of-domain sampling phase. +//! Out-of-domain sampling IOR. //! -//! Paired spec: `docs/paper-mods/mod1_oracle.tex`. This phase is a thin +//! Paired spec: `docs/paper-mods/mod1_oracle.tex`. This IOR is a thin //! composition of point queries on the committed oracle — see //! [`Oracle::query_at_point`](crate::protocol::oracle::Oracle::query_at_point). //! The verifier derives the same random points from the transcript. @@ -49,7 +49,7 @@ pub struct OodReducedStatement { pub answers: Vec, } -/// OOD phase configuration. Stateless; the lifetime parameter exists only +/// OOD IOR configuration. Stateless; the lifetime parameter exists only /// to anchor `ProverInputs<'a>` for the trait impl. pub struct Ood<'a, F: Field> { pub _phantom: PhantomData<&'a F>, diff --git a/src/protocol/iors/oracle_handle.rs b/src/protocol/iors/oracle_handle.rs index 51f5594..fcfb646 100644 --- a/src/protocol/iors/oracle_handle.rs +++ b/src/protocol/iors/oracle_handle.rs @@ -1,24 +1,22 @@ //! Oracle access via handle traits. //! -//! Phase verifiers should code against [`IndexedOracle`] rather than -//! against a concrete BCS-shaped tuple of `(root, opening proof, -//! precomputed answer table)`. The handle internally validates the -//! commitment-scheme opening and exposes a partial-function view of the -//! committed oracle: +//! IOR verifiers code against [`IndexedOracle`] rather than against a +//! concrete BCS-shaped tuple of `(root, opening proof, precomputed answer +//! table)`. The handle internally validates the commitment-scheme opening +//! and exposes a partial-function view of the committed oracle: //! //! ```text //! query(i) -> Some(value) iff index in range AND opening passes //! query(i) -> None otherwise //! ``` //! -//! This matches the IOP/BCS formalism where oracles are partial -//! functions; the BCS instantiation (Merkle root + path-pruned -//! multi-opening proof + the values that get authenticated) lives behind -//! the trait, not in the phase verifier code. +//! Matches the IOP/BCS formalism where oracles are partial functions; +//! the BCS instantiation (Merkle root + path-pruned multi-opening proof + +//! authenticated values) lives behind the trait, not in IOR verifier code. //! //! Currently used by [`super::proximity`] on the verifier side. Other -//! phases continue to use concrete oracle types for now; the migration -//! of OOD / Batching prover-side `Oracle` access to a sibling +//! IORs continue to use concrete oracle types for now; migration of +//! OOD / Batching prover-side `Oracle` access to a sibling //! `EvalOracle` trait is tracked as a follow-up. use std::cell::OnceCell; @@ -35,7 +33,7 @@ use crate::crypto::merkle::{WarpProof, WarpScheme}; /// range" and "opening failed validation." pub trait IndexedOracle { fn query(&self, i: usize) -> Option; - /// Optional eager validation hook. Phases that want a single + /// Optional eager validation hook. Callers that want a single /// up-front check (rather than lazy per-query) call `validate()` /// once. The default impl performs a no-op `query(0)` to drive /// whatever lazy validation the impl uses. diff --git a/src/protocol/iors/pesat.rs b/src/protocol/iors/pesat.rs index c4c5049..2b25d95 100644 --- a/src/protocol/iors/pesat.rs +++ b/src/protocol/iors/pesat.rs @@ -1,9 +1,8 @@ -//! PESAT Reduction phase. +//! PESAT Reduction IOR. //! -//! Implements Phase 2 of the Warp prover: encode fresh witnesses into -//! codewords, commit via a multi-vector Merkle tree (one root over all -//! l1 codewords), absorb commitment + code evaluations, and derive the -//! τ zero-check challenges. +//! Encodes fresh witnesses into codewords, commits via a multi-vector +//! Merkle tree (one root over all l1 codewords), absorbs commitment + +//! code evaluations, and derives the τ zero-check challenges. //! //! IOR signature //! ------------- @@ -60,7 +59,7 @@ pub struct PesatVerifierOutputs { pub rt_0: H::Digest, } -/// PESAT phase configuration. +/// PESAT IOR configuration. pub struct Pesat<'a, F, C, H> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, diff --git a/src/protocol/iors/proximity.rs b/src/protocol/iors/proximity.rs index 349846d..84913fc 100644 --- a/src/protocol/iors/proximity.rs +++ b/src/protocol/iors/proximity.rs @@ -1,4 +1,4 @@ -//! Proximity / shift-query phase. +//! Proximity / shift-query IOR. //! //! Index queries on the committed oracles. Opens both the fresh PESAT //! commitment and each accumulated commitment at the query positions, @@ -50,9 +50,9 @@ where /// Verifier-side inputs for Proximity. /// -/// The phase verifier no longer sees roots / opening proofs / answer +/// The IOR verifier no longer sees roots / opening proofs / answer /// tables directly; it sees [`IndexedOracle`] handles and triggers -/// their (lazy, memoized) BCS validation. Phases stay BCS-agnostic at +/// their (lazy, memoized) BCS validation. IORs stay BCS-agnostic at /// the type level — the orchestrator picks the concrete handle type /// (today: [`oracle_handle::MerkleIndexedOracle`]). /// @@ -84,7 +84,7 @@ where pub shift_query_answers: Vec>, } -/// Proximity phase configuration. Holds the hasher value used by both +/// Proximity IOR configuration. Holds the hasher value used by both /// `open` (prover side) and `check` (verifier side). pub struct Proximity<'a, F, H> where @@ -170,7 +170,7 @@ where // ark-mt's `open()` requires strictly-sorted, unique indices. Query // positions can repeat or arrive unsorted; deduplicate for the // merkle opening, but keep `shift_query_answers` in original query - // order so downstream phases (batching) can index by query. + // order so Batching can index by query. let mut sorted_unique = leaf_positions.clone(); sorted_unique.sort_unstable(); sorted_unique.dedup(); @@ -250,14 +250,12 @@ where 'a: 'c, H: 'c, { - // Arity / shape checks (orchestrator-side concerns the phase still - // owns: number of accumulator openings must match l2; number of - // shift queries is implicitly checked by the handle constructors). + // Arity check: number of accumulator openings must match l2. (inputs.acc.len() == statement.l2).then_some(()).ok_or(VerifierError::NumL2Instances)?; - // Validate each oracle handle. The handle is a trait-object - // partial function: validate() runs the (lazy, memoized) BCS - // check internally — this phase code is BCS-agnostic. + // Validate each oracle handle. The handle is a partial function: + // validate() runs the (lazy, memoized) BCS check internally — + // this IOR stays BCS-agnostic. inputs .fresh .validate() diff --git a/src/protocol/iors/sample_queries.rs b/src/protocol/iors/sample_queries.rs index b2a23c8..24685ea 100644 --- a/src/protocol/iors/sample_queries.rs +++ b/src/protocol/iors/sample_queries.rs @@ -1,4 +1,4 @@ -//! Shift-query sampling phase. +//! Shift-query sampling IOR. //! //! Between OOD and Batching, the verifier draws `t · log_n` random //! bits and decodes them into `t` shift-query positions over @@ -41,7 +41,7 @@ pub struct SampleQueriesReducedStatement { pub queries: QueryIndices, } -/// Shift-query sampling phase configuration. Stateless. +/// Shift-query sampling IOR configuration. Stateless. pub struct SampleQueries<'a, F: Field> { pub _phantom: PhantomData<&'a F>, } diff --git a/src/protocol/iors/twin_constraint.rs b/src/protocol/iors/twin_constraint.rs index 2f137cd..dd31840 100644 --- a/src/protocol/iors/twin_constraint.rs +++ b/src/protocol/iors/twin_constraint.rs @@ -1,8 +1,8 @@ -//! Twin-constraint sumcheck phase. +//! Twin-constraint sumcheck IOR. //! //! Paired spec: `docs/paper-mods/mod1_oracle.tex` (oracle composition). //! The forthcoming `docs/paper-mods/mod2_structured_sumcheck.tex` will -//! promote this phase's fused-fold prover to a first-class paper primitive. +//! promote this IOR's fused-fold prover to a first-class paper primitive. //! //! Reduces the claim //! @@ -212,7 +212,7 @@ pub struct TwinConstraintProverInputs<'a, F: Field> { /// actual oracle check `final_claim ≟ eq(τ, γ) · (ν₀ + ω·η)` cannot be /// completed inside `TwinConstraint::verify` because ν₀ and η arrive on the /// transcript *after* the sumcheck rounds. Rather than splitting -/// TwinConstraint into two IORs (which cascades into other phases having +/// TwinConstraint into two IORs (which cascades into other IORs having /// similar shapes), we expose the obligation as a typed value. /// /// Discharge by calling [`Self::discharge`] with the missing inputs once the @@ -273,7 +273,7 @@ pub struct TwinConstraintReducedWitness { pub z: Vec, } -/// TwinConstraint phase configuration. +/// TwinConstraint IOR configuration. pub struct TwinConstraint<'a, F, H> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, diff --git a/src/protocol/oracle.rs b/src/protocol/oracle.rs index 966582f..928b492 100644 --- a/src/protocol/oracle.rs +++ b/src/protocol/oracle.rs @@ -1,18 +1,18 @@ -//! Oracle abstraction for Warp's IOR phases. +//! Oracle abstraction for Warp's IORs. //! //! Paired spec: `docs/paper-mods/mod1_oracle.tex`. //! -//! An [`Oracle`] is a single object that carries both views Warp's phases -//! need of a committed codeword: the raw evaluation table `f: [n] → F` -//! (BCS-native, index-queryable) and the implied multilinear extension +//! An [`Oracle`] carries both views Warp's IORs need of a committed +//! codeword: the raw evaluation table `f: [n] → F` (BCS-native, +//! index-queryable) and the implied multilinear extension //! `\hat f: F^{log n} → F` (point-queryable). The multilinear extension is //! materialised lazily on first point query and cached. //! //! The Merkle commitment of the codeword is **not** held here. In PESAT a -//! single Merkle tree covers many interleaved codewords -//! (`src/crypto/merkle/mod.rs::build_codeword_leaves`), so the tree is -//! tracked by the enclosing data structure (`PesatOutput`, -//! `AccumulatorWitness`) rather than 1:1 with the oracle. See the +//! single Merkle tree covers many interleaved codewords (see +//! `src/crypto/merkle/mod.rs::build_codeword_leaves`), so the tree is +//! tracked by the enclosing data structure (`AccumulatorWitness`, the +//! PESAT reduced witness) rather than 1:1 with the oracle. See the //! Implementation note in `mod1_oracle.tex` §2. use ark_ff::Field; diff --git a/src/types.rs b/src/types.rs index 0f0b967..3713e59 100644 --- a/src/types.rs +++ b/src/types.rs @@ -34,7 +34,7 @@ pub type ProveResult = Result< ProverError, >; -/// Protocol parameters for WARP — the shared configuration used by all IOR phases. +/// Protocol parameters for WARP — the shared configuration used by all IORs. pub struct WARPParams, C: LinearCode + Clone, H: MerkleHasher> { pub(crate) _f: PhantomData, pub config: WARPConfig, @@ -94,43 +94,6 @@ impl AccumulatorInstance { self } - /// Push a single typed entry. Pivots `AccumulatorEntry` (AoS) into the - /// internal parallel-Vec (SoA) layout. - pub fn push_entry(&mut self, entry: AccumulatorEntry) - where - H::Digest: Clone, - { - self.rt.push(entry.rt); - self.alpha.push(entry.alpha); - self.mu.push(entry.mu); - self.beta.0.push(entry.tau); - self.beta.1.push(entry.x); - self.eta.push(entry.eta); - } - - /// Construct from an explicit list of typed entries. - pub fn from_entries(entries: Vec>) -> Self - where - H::Digest: Clone, - { - let mut acc = Self::empty(); - for entry in entries { - acc.push_entry(entry); - } - acc - } -} - -/// One accumulated claim, AoS view of `AccumulatorInstance`. Use this -/// when constructing entries one at a time; pipe through -/// [`AccumulatorInstance::push_entry`] / [`AccumulatorInstance::from_entries`]. -pub struct AccumulatorEntry { - pub rt: H::Digest, - pub alpha: Vec, - pub mu: F, - pub tau: Vec, - pub x: Vec, - pub eta: F, } /// Accumulator witness — the private part of an accumulated claim. @@ -188,30 +151,6 @@ where self.w.extend(other.w); self } - - pub fn push_entry(&mut self, entry: AccumulatorWitnessEntry) { - self.td.push(entry.td); - self.w.push(entry.w); - } - - pub fn from_entries(entries: Vec>) -> Self { - let mut acc = Self::empty(); - for entry in entries { - acc.push_entry(entry); - } - acc - } -} - -/// One witness entry for an accumulated claim — AoS counterpart of -/// `AccumulatorWitness`. -pub struct AccumulatorWitnessEntry -where - F: Field, - H: MerkleHasher>, -{ - pub td: crate::crypto::merkle::WarpCommitted, - pub w: Vec, } /// Proof produced by the WARP accumulation prover. @@ -256,20 +195,3 @@ where } } -/// Intermediate output of the PESAT reduction phase. -/// -/// This data flows from Phase 2 (PESAT Reduction) into Phase 3 (Constrained Code Accumulation). -pub struct PesatOutput -where - F: Field, - H: MerkleHasher>, -{ - /// Encoded codewords from fresh witnesses. - pub codewords: Vec>, - /// Multi-vector commitment over the `l1` codewords. - pub td_0: WarpCommitted, - /// Code evaluation claims: `f_i(0)` for each codeword. - pub mus: Vec, - /// PESAT evaluation challenges (one per fresh instance). - pub taus: Vec>, -} From fc1b41daf89e272cbadc1d6a5855700cc4eac6ce Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 12 May 2026 15:15:59 +0200 Subject: [PATCH 57/68] refactor: directory layout teaches "WARP = choreography of IORs" Moves the IOR trait to src/protocol/ior.rs (its own conceptual anchor), demotes src/protocol/iors/mod.rs to an ordered list of concrete IORs. Promotes oracle vocabulary under src/protocol/oracles/ (evaluation, indexed_merkle, query_indices). Splits the bulky lib.rs into src/warp/ (scheme + prove + verify + decide + accumulator + proof + params + keys), leaving lib.rs as a table of contents. Renames traits.rs to accumulation.rs to name the abstraction it carries. Trims README and profiling example to match. No behavior change; 22 lib + 2 integration + 9 verifier-negative tests still pass. --- README.md | 16 +- benches/warp_rs.rs | 8 +- .../{profile_phases.rs => profile_iors.rs} | 26 +- src/{traits.rs => accumulation.rs} | 5 +- src/lib.rs | 657 +----------------- src/protocol/ior.rs | 162 +++++ src/protocol/iors/batching.rs | 4 +- src/protocol/iors/bridge.rs | 4 +- src/protocol/iors/mod.rs | 201 +----- src/protocol/iors/ood.rs | 6 +- src/protocol/iors/pesat.rs | 2 +- src/protocol/iors/proximity.rs | 20 +- src/protocol/iors/sample_queries.rs | 4 +- src/protocol/iors/twin_constraint.rs | 6 +- src/protocol/mod.rs | 4 +- .../{oracle.rs => oracles/evaluation.rs} | 0 .../indexed_merkle.rs} | 0 src/protocol/oracles/mod.rs | 6 + .../{query.rs => oracles/query_indices.rs} | 0 src/protocol/transcript/prover.rs | 2 +- src/protocol/transcript/verifier.rs | 2 +- src/serialize.rs | 2 +- src/types.rs | 197 ------ src/warp/accumulator.rs | 96 +++ src/warp/decide.rs | 67 ++ src/warp/keys.rs | 16 + src/warp/mod.rs | 16 + src/warp/params.rs | 16 + src/warp/proof.rs | 46 ++ src/warp/prove.rs | 289 ++++++++ src/warp/scheme.rs | 101 +++ src/warp/verify.rs | 283 ++++++++ tests/integration_warp.rs | 4 +- tests/profile_json.rs | 4 +- tests/verifier_negative.rs | 4 +- 35 files changed, 1162 insertions(+), 1114 deletions(-) rename examples/{profile_phases.rs => profile_iors.rs} (87%) rename src/{traits.rs => accumulation.rs} (83%) create mode 100644 src/protocol/ior.rs rename src/protocol/{oracle.rs => oracles/evaluation.rs} (100%) rename src/protocol/{iors/oracle_handle.rs => oracles/indexed_merkle.rs} (100%) create mode 100644 src/protocol/oracles/mod.rs rename src/protocol/{query.rs => oracles/query_indices.rs} (100%) delete mode 100644 src/types.rs create mode 100644 src/warp/accumulator.rs create mode 100644 src/warp/decide.rs create mode 100644 src/warp/keys.rs create mode 100644 src/warp/mod.rs create mode 100644 src/warp/params.rs create mode 100644 src/warp/proof.rs create mode 100644 src/warp/prove.rs create mode 100644 src/warp/scheme.rs create mode 100644 src/warp/verify.rs diff --git a/README.md b/README.md index adc0dd7..f85edcf 100644 --- a/README.md +++ b/README.md @@ -51,15 +51,19 @@ for batch in batches { warp.decide(acc_w, acc_x)?; ``` -The `examples/profile_phases.rs` runs the full pipeline and prints a -per-phase wall-time breakdown when built with `--features profile`. +`examples/profile_iors.rs` runs the full prover and prints a per-IOR +wall-time breakdown when built with `--features profile`. ## Layout -- `src/lib.rs` — `WARP::{prove, verify, decide}` orchestrator -- `src/protocol/phases/` — IOR phases (`pesat`, `twin_constraint`, `ood`, `batching`, `proximity`) +- `src/warp/` — `WARP::{prove, verify, decide}` choreography over IORs +- `src/protocol/ior.rs` — the `IOR` trait +- `src/protocol/iors/` — concrete IORs (`pesat`, `twin_constraint`, `bridge`, `ood`, `sample_queries`, `batching`, `proximity`) +- `src/protocol/oracles/` — oracle vocabulary used by IORs +- `src/protocol/transcript/` — transcript absorb / parse helpers +- `src/accumulation.rs` — the `AccumulationScheme` trait - `src/relations/` — `R1CS`, `BundledPESAT`, `HashChainRelation` -- `src/crypto/`, `src/utils/` — Merkle wrapper, field/poly helpers +- `src/crypto/`, `src/utils/` — Merkle wrapper, field / poly helpers - `src/profile/` — opt-in tracing layer (gated behind the `profile` feature) - `tests/integration_warp.rs` — end-to-end on BLS12-381 and Goldilocks - `tests/verifier_negative.rs` — single-tamper rejection tests @@ -69,5 +73,5 @@ per-phase wall-time breakdown when built with `--features profile`. ```sh cargo test --release cargo bench -cargo run --release --features profile --example profile_phases +cargo run --release --features profile --example profile_iors ``` diff --git a/benches/warp_rs.rs b/benches/warp_rs.rs index 987d944..a342a93 100644 --- a/benches/warp_rs.rs +++ b/benches/warp_rs.rs @@ -8,8 +8,8 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use utils::domainsep::init_prover_state; use utils::hash_chain::{get_hashchain_instance_witness_pairs, get_hashchain_r1cs}; use warp::config::WARPConfig; -use warp::traits::AccumulationScheme; -use warp::types::WARPProverKey; +use warp::accumulation::AccumulationScheme; +use warp::warp::WARPProverKey; use warp::WARP; mod utils; @@ -61,8 +61,8 @@ pub fn bench_rs_warp_fields(c: &mut Criterion) { &mut prover_state, instances_witnesses.1.clone(), instances_witnesses.0.clone(), - warp::types::AccumulatorInstance::empty(), - warp::types::AccumulatorWitness::empty(), + warp::warp::AccumulatorInstance::empty(), + warp::warp::AccumulatorWitness::empty(), ) .unwrap(); }, diff --git a/examples/profile_phases.rs b/examples/profile_iors.rs similarity index 87% rename from examples/profile_phases.rs rename to examples/profile_iors.rs index 7eef227..df99277 100644 --- a/examples/profile_phases.rs +++ b/examples/profile_iors.rs @@ -1,25 +1,13 @@ -//! Per-phase wall-time profile of a Goldilocks WARP prove run — used to -//! report the micro-profile table after the rewrite-v2 effsc integration. -//! See the original shape in the PR #22 body. +//! Per-IOR wall-time profile of a Goldilocks WARP prove run. //! -//! Run: -//! ```text -//! cargo run --release --features profile --example profile_phases -//! ``` -//! -//! The `profile` feature installs the JSON layer from `warp::profile::init_json`. -//! This binary captures the emitted records, aggregates wall_ns per phase -//! over a handful of prove invocations, and prints a markdown table that -//! mirrors the PR #22 breakdown (rs_encode, pesat_merkle_tree, -//! twin_constraint_sumcheck, etc.). -//! -//! Best run with `RAYON_NUM_THREADS` pinned and all other workloads off. +//! Run: `cargo run --release --features profile --example profile_iors`. +//! Best run with `RAYON_NUM_THREADS` pinned and other workloads off. #[cfg(not(feature = "profile"))] fn main() { eprintln!( - "profile_phases: build with --features profile (and preferably --release).\n\ - example: cargo run --release --features profile --example profile_phases" + "profile_iors: build with --features profile (and preferably --release).\n\ + example: cargo run --release --features profile --example profile_iors" ); } @@ -51,8 +39,8 @@ mod inner { }, BundledPESAT, Relation, ToPolySystem, }; - use warp::traits::AccumulationScheme; - use warp::types::{AccumulatorInstance, AccumulatorWitness, WARPProverKey}; + use warp::accumulation::AccumulationScheme; + use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProverKey}; use warp::utils::fields::Goldilocks; use warp::utils::poseidon; use warp::WARP; diff --git a/src/traits.rs b/src/accumulation.rs similarity index 83% rename from src/traits.rs rename to src/accumulation.rs index 484bbad..3e3bc54 100644 --- a/src/traits.rs +++ b/src/accumulation.rs @@ -3,7 +3,8 @@ use ark_mt::MerkleHasher; use spongefish::{ProverState, VerificationResult, VerifierState}; use crate::error::{VerifierError, WARPError}; -use crate::types::{AccumulatorInstance, AccumulatorWitness, ProveResult, WARPProof}; +use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; +use crate::warp::proof::{ProveResult, WARPProof}; pub trait AccumulationScheme>> { type Index; @@ -12,13 +13,11 @@ pub trait AccumulationScheme>> { type Instances; type Witnesses; - // on given index, returns prover and verifier keys fn index( prover_state: &mut ProverState, index: Self::Index, ) -> VerificationResult<(Self::ProverKey, Self::VerifierKey)>; - // prove accumulation of instances and witnesses with previous accumulators `accs` fn prove( &self, pk: Self::ProverKey, diff --git a/src/lib.rs b/src/lib.rs index f17c774..c3c7c07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,24 +1,6 @@ #![allow(clippy::assign_op_pattern)] // generated by SmallFpConfig derive macro -use crate::error::WARPError; -use crate::traits::AccumulationScheme; -use crate::types::{ - AccumulatorInstance, AccumulatorWitness, ProveResult, WARPParams, WARPProof, WARPProverKey, - WARPVerifierKey, -}; -use ark_codes::traits::LinearCode; -use ark_ff::{Field, PrimeField}; -use ark_mt::MerkleHasher; -use ark_poly::{DenseMultilinearExtension, Polynomial}; -use ark_std::log2; -use config::WARPConfig; -use effsc::hypercube::compute_hypercube_eq_evals; -use protocol::transcript::{absorb_instances, parse_statement}; -use relations::{r1cs::R1CSConstraints, BundledPESAT}; -use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; -use std::marker::PhantomData; -use utils::scale_and_sum; -use utils::concat_slices; +pub mod accumulation; pub mod config; pub mod constraints; pub mod crypto; @@ -28,645 +10,18 @@ pub mod profile; pub mod protocol; pub mod relations; pub mod serialize; -pub mod traits; -pub mod types; pub mod utils; +pub mod warp; /// Re-exports the handful of items every WARP consumer needs. Use as /// `use warp::prelude::*;`. pub mod prelude { + pub use crate::accumulation::AccumulationScheme; pub use crate::config::WARPConfig; pub use crate::error::{DeciderError, ProverError, VerifierError, WARPError}; - pub use crate::traits::AccumulationScheme; - pub use crate::types::{ - AccumulatorInstance, AccumulatorWitness, WARPProof, WARPProverKey, WARPVerifierKey, + pub use crate::warp::{ + AccumulatorInstance, AccumulatorWitness, WARPProof, WARPProverKey, WARPVerifierKey, WARP, }; - pub use crate::WARP; } -use crate::crypto::merkle::warp_scheme; -use error::{DeciderError, ProverError, VerifierError}; -use protocol::iors::{ - batching::{ - Batching, BatchingProverInputs, BatchingReducedStatement, BatchingReducedWitness, - BatchingStatement, BatchingVerifierInputs, - }, - bridge::{ - Bridge, BridgeProverInputs, BridgeReducedStatement, BridgeReducedWitness, BridgeStatement, - BridgeVerifierInputs, BridgeWitness, - }, - ood::{Ood, OodProverInputs, OodReducedStatement, OodStatement}, - oracle_handle::MerkleIndexedOracle, - pesat::{ - Pesat, PesatReducedStatement, PesatReducedWitness, PesatStatement, PesatVerifierOutputs, - PesatWitness, - }, - proximity::{ - Proximity, ProximityProofString, ProximityProverInputs, ProximityStatement, - ProximityVerifierInputs, - }, - sample_queries::{SampleQueries, SampleQueriesReducedStatement, SampleQueriesStatement}, - twin_constraint::{ - TwinConstraint, TwinConstraintProverInputs, TwinConstraintReducedStatement, - TwinConstraintReducedWitness, TwinConstraintStatement, TwinConstraintWitness, - }, - IorProveResult, IorVerifyResult, -}; - -pub struct WARP, C: LinearCode + Clone, H: MerkleHasher> { - pub params: WARPParams, -} - -impl WARP -where - F: Field, - P: Clone + BundledPESAT, - C: LinearCode + Clone, - H: MerkleHasher>, -{ - pub fn new(config: WARPConfig, code: C, p: P, hasher: H) -> WARP { - Self { - params: WARPParams { - _f: PhantomData, - config, - code, - p, - hasher, - }, - } - } -} - -impl AccumulationScheme for WARP -where - F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - P: Clone + BundledPESAT, Config = (usize, usize, usize)>, - C: LinearCode + Clone, - H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, -{ - type Index = P; - type ProverKey = WARPProverKey

    ; - type VerifierKey = WARPVerifierKey; - type Instances = Vec>; - type Witnesses = Vec>; - - fn index( - prover_state: &mut ProverState, - index: Self::Index, - ) -> spongefish::VerificationResult<(Self::ProverKey, Self::VerifierKey)> { - let (m, n, k) = index.config(); - prover_state.public_message(&index.description()); - prover_state.prover_message(&F::from(m as u32)); - prover_state.prover_message(&F::from(n as u32)); - prover_state.prover_message(&F::from(k as u32)); - Ok(( - WARPProverKey { - index, - m, - n, - k, - }, - WARPVerifierKey { m, n, k }, - )) - } - - #[tracing::instrument(name = "warp.prove", skip_all)] - fn prove( - &self, - pk: Self::ProverKey, - prover_state: &mut ProverState, - witnesses: Self::Witnesses, - instances: Self::Instances, - acc_instance: AccumulatorInstance, - acc_witness: AccumulatorWitness, - ) -> ProveResult { - if instances.len() < 2 { - return Err(ProverError::InsufficientInstances { - got: instances.len(), - }); - } - if witnesses.len() != instances.len() { - return Err(ProverError::InstanceWitnessLengthMismatch { - instances: instances.len(), - witnesses: witnesses.len(), - }); - } - if acc_witness.td.len() != acc_instance.rt.len() { - return Err(ProverError::AccumulatorShapeMismatch { - instances: acc_instance.rt.len(), - roots: acc_witness.td.len(), - }); - } - - let (l1, l) = (self.params.config.l1, self.params.config.l); - let l2 = l - l1; - if !l.is_power_of_two() { - return Err(ProverError::ConfigParameterInvalid { - reason: format!("config.l = {l} is not a power of two"), - }); - } - - #[allow(non_snake_case)] - let (M, N, k) = (pk.m, pk.n, pk.k); - let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); - let n = self.params.code.code_len(); - let log_n = log2(n) as usize; - - if instances[0].len() != N - k { - return Err(ProverError::InstanceLengthMismatch { - expected: N - k, - got: instances[0].len(), - }); - } - absorb_instances(prover_state, &instances); - acc_instance.absorb_into(prover_state); - - // Extract the single codeword per accumulator entry for twin_constraint. - let AccumulatorWitness { - td: acc_tds, - w: acc_ws, - } = acc_witness; - let acc_fs: Vec> = acc_tds.iter().map(|td| td.codewords()[0].clone()).collect(); - - // IOR instances. - let pesat_ior = Pesat:: { - code: &self.params.code, - hasher: &self.params.hasher, - _phantom: PhantomData, - }; - let twin_constraint_ior = TwinConstraint:: { - r1cs: self.params.p.constraints(), - _phantom: PhantomData, - }; - let bridge_ior = Bridge::::new(); - let ood_ior = Ood::::new(); - let sample_queries_ior = SampleQueries::::new(); - let batching_ior = Batching::::new(); - let proximity_ior = Proximity:: { - hasher: &self.params.hasher, - _phantom: PhantomData, - }; - - // ── IOR 1: PESAT — encode witnesses, commit, derive τ ──────── - let IorProveResult { - reduced: PesatReducedStatement { mus, taus }, - proof: _, - witness: PesatReducedWitness { codewords, td_0 }, - } = prove_ior!( - pesat_ior, - prover_state, - statement: PesatStatement { l1, log_m }, - witness: PesatWitness { witnesses: &witnesses }, - inputs: (), - )?; - - // ── IOR 2: TwinConstraint — fused-fold sumcheck ────────────── - let IorProveResult { - reduced: TwinConstraintReducedStatement { - gamma: _, - zeta_0, - beta_tau, - deferred: _, - }, - proof: _, - witness: TwinConstraintReducedWitness { f, z }, - } = prove_ior!( - twin_constraint_ior, - prover_state, - statement: TwinConstraintStatement { - acc_instance, - l1_mus: mus.clone(), - l1_taus: taus, - log_l, - log_m, - log_n, - }, - witness: TwinConstraintWitness { - acc_witness_w: &acc_ws, - instances: &instances, - witnesses: &witnesses, - }, - inputs: TwinConstraintProverInputs { - fresh_codewords: &codewords, - acc_codewords: &acc_fs, - }, - )?; - - // ── IOR 3: Bridge — publish (td_new, η, ν₀); split z ───────── - let IorProveResult { - reduced: BridgeReducedStatement { - eta, - nu_0, - td_new_root: _, - }, - proof: _, - witness: BridgeReducedWitness { td_new, new_x, new_w }, - } = prove_ior!( - bridge_ior, - prover_state, - statement: BridgeStatement { - zeta_0: zeta_0.clone(), - beta_tau: beta_tau.clone(), - log_m, - n_minus_k: N - k, - }, - witness: BridgeWitness { z: &z, f: &f }, - inputs: BridgeProverInputs { - bundled_pesat: &self.params.p, - hasher: &self.params.hasher, - code_len: n, - _f: PhantomData, - }, - )?; - - // ── IOR 4: OOD — out-of-domain queries on the reduced oracle ── - let IorProveResult { - reduced: OodReducedStatement { samples_flat, answers }, - proof: _, - witness: _, - } = prove_ior!( - ood_ior, - prover_state, - statement: OodStatement { s: self.params.config.s, log_n }, - witness: (), - inputs: OodProverInputs { oracle: &f }, - )?; - - // ── IOR 5: SampleQueries — sample t shift positions ────────── - let IorProveResult { - reduced: SampleQueriesReducedStatement { queries }, - proof: _, - witness: _, - } = prove_ior!( - sample_queries_ior, - prover_state, - statement: SampleQueriesStatement { log_n, t: self.params.config.t }, - witness: (), - inputs: (), - )?; - - // ── IOR 6: Batching — DAG fan-in over (ζ₀, OOD, queries) ───── - let IorProveResult { - reduced: BatchingReducedStatement { alpha }, - proof: _, - witness: BatchingReducedWitness { mu }, - } = prove_ior!( - batching_ior, - prover_state, - statement: BatchingStatement::from_ior_outputs( - zeta_0.clone(), - &samples_flat, - &queries.evaluation_points, - self.params.config.s, - self.params.config.t, - log_n, - ), - witness: (), - inputs: BatchingProverInputs { oracle: &f }, - )?; - - // ── IOR 7: Proximity — open committed oracles at shift positions ── - let IorProveResult { - reduced: _, - proof: ProximityProofString { - auth_0, - auth_j, - shift_query_answers, - }, - witness: _, - } = prove_ior!( - proximity_ior, - prover_state, - statement: ProximityStatement { - queries: queries.clone(), - l2, - t: self.params.config.t, - n, - }, - witness: (), - inputs: ProximityProverInputs { - td_0: &td_0, - acc_td: &acc_tds, - }, - )?; - - // ── Assemble new accumulator state and proof ───────────────── - let mut nus = Vec::with_capacity(1 + self.params.config.s); - nus.push(nu_0); - nus.extend(answers); - - let new_acc_instance = AccumulatorInstance { - rt: vec![td_new.root().clone()], - alpha: vec![alpha], - mu: vec![mu], - beta: (vec![beta_tau], vec![new_x]), - eta: vec![eta], - }; - let new_acc_witness = AccumulatorWitness { - td: vec![td_new], - w: vec![new_w], - }; - let proof = WARPProof { - rt_0: td_0.root().clone(), - mu_i: mus, - nu_0, - nu_i: nus, - auth_0, - auth_j, - shift_query_answers, - }; - - Ok(((new_acc_instance, new_acc_witness), proof)) - } - - fn verify<'a>( - &self, - vk: Self::VerifierKey, - verifier_state: &mut VerifierState<'a>, - acc_instance: AccumulatorInstance, - proof: WARPProof, - ) -> Result<(), VerifierError> { - let (l1, l) = (self.params.config.l1, self.params.config.l); - let l2 = l - l1; - - #[allow(non_snake_case)] - let (M, N, k) = (vk.m, vk.n, vk.k); - let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); - let n = self.params.code.code_len(); - let log_n = log2(n) as usize; - - // Parse the transcript-side statement (l1 fresh instances + l2 - // accumulated entries). - let (l1_xs, parsed_acc) = - parse_statement::(verifier_state, l1, l2, N - k, log_n, log_m)?; - - // Cache prior-accumulator values for the post-IOR (α, β) consistency checks. - let acc_alpha_first = acc_instance.alpha[0].clone(); - let acc_beta_0_first = acc_instance.beta.0[0].clone(); - let acc_beta_1_first = acc_instance.beta.1[0].clone(); - let acc_mu_first = acc_instance.mu[0]; - let l2_taus = parsed_acc.beta.0.clone(); - let l2_xs = parsed_acc.beta.1.clone(); - - // Cache the accumulated roots before `parsed_acc` moves into TC's - // statement — Proximity needs them for the per-acc Merkle handles. - let l2_roots = parsed_acc.rt.clone(); - - // IOR instances. - let pesat_ior = Pesat:: { - code: &self.params.code, - hasher: &self.params.hasher, - _phantom: PhantomData, - }; - let twin_constraint_ior = TwinConstraint:: { - r1cs: self.params.p.constraints(), - _phantom: PhantomData, - }; - let bridge_ior = Bridge::::new(); - let ood_ior = Ood::::new(); - let sample_queries_ior = SampleQueries::::new(); - let batching_ior = Batching::::new(); - let proximity_ior = Proximity:: { - hasher: &self.params.hasher, - _phantom: PhantomData, - }; - - // ── IOR 1: PESAT::verify ───────────────────────────────────── - let IorVerifyResult { - reduced: PesatReducedStatement { - mus: l1_mus, - taus: l1_taus, - }, - outputs: PesatVerifierOutputs { rt_0 }, - } = verify_ior!( - pesat_ior, - verifier_state, - statement: PesatStatement { l1, log_m }, - inputs: (), - )?; - - // ── IOR 2: TwinConstraint::verify ──────────────────────────── - let IorVerifyResult { - reduced: TwinConstraintReducedStatement { - gamma, - zeta_0, - beta_tau, - deferred, - }, - outputs: _, - } = verify_ior!( - twin_constraint_ior, - verifier_state, - statement: TwinConstraintStatement { - acc_instance: parsed_acc, - l1_mus: l1_mus.clone(), - l1_taus: l1_taus.clone(), - log_l, - log_m, - log_n, - }, - inputs: (), - )?; - - // ── IOR 3: Bridge::verify — read (td, η, ν₀); discharge deferred ─ - let IorVerifyResult { - reduced: BridgeReducedStatement { - eta: _, - nu_0, - td_new_root: _, - }, - outputs: _, - } = verify_ior!( - bridge_ior, - verifier_state, - statement: BridgeStatement { - zeta_0: zeta_0.clone(), - beta_tau, - log_m, - n_minus_k: N - k, - }, - inputs: BridgeVerifierInputs { - deferred: &deferred, - gamma: &gamma, - }, - )?; - - // ── IOR 4: OOD::verify ─────────────────────────────────────── - let IorVerifyResult { - reduced: OodReducedStatement { samples_flat, answers }, - outputs: _, - } = verify_ior!( - ood_ior, - verifier_state, - statement: OodStatement { s: self.params.config.s, log_n }, - inputs: (), - )?; - - // ── IOR 5: SampleQueries::verify ───────────────────────────── - let IorVerifyResult { - reduced: SampleQueriesReducedStatement { queries }, - outputs: _, - } = verify_ior!( - sample_queries_ior, - verifier_state, - statement: SampleQueriesStatement { log_n, t: self.params.config.t }, - inputs: (), - )?; - - // ── Whole-proof arity checks ───────────────────────────────── - (proof.shift_query_answers.len() == self.params.config.t) - .then_some(()) - .ok_or(VerifierError::NumShiftQueries)?; - (proof.auth_j.len() == l2) - .then_some(()) - .ok_or(VerifierError::NumL2Instances)?; - - // ── Build IndexedOracle handles for Proximity ─────────────── - let mut indexed: Vec<(usize, usize)> = queries - .leaf_positions - .iter() - .copied() - .enumerate() - .map(|(row, pos)| (pos, row)) - .collect(); - indexed.sort_by_key(|&(pos, _)| pos); - indexed.dedup_by_key(|&mut (pos, _)| pos); - let sorted_unique: Vec = indexed.iter().map(|&(p, _)| p).collect(); - let row_indices: Vec = indexed.iter().map(|&(_, r)| r).collect(); - - let fresh_values: Vec> = row_indices - .iter() - .map(|&r| proof.shift_query_answers[r][l2..].to_vec()) - .collect(); - let fresh_handle = MerkleIndexedOracle::new( - warp_scheme::(self.params.hasher.clone(), n), - &rt_0, - &proof.auth_0, - sorted_unique.clone(), - fresh_values, - ); - - let acc_handles: Vec> = (0..l2) - .map(|j| { - let acc_values: Vec> = row_indices - .iter() - .map(|&r| vec![proof.shift_query_answers[r][j]]) - .collect(); - MerkleIndexedOracle::new( - warp_scheme::(self.params.hasher.clone(), n), - &l2_roots[j], - &proof.auth_j[j], - sorted_unique.clone(), - acc_values, - ) - }) - .collect(); - - // ── IOR 7: Proximity::verify ───────────────────────────────── - verify_ior!( - proximity_ior, - verifier_state, - statement: ProximityStatement { - queries: queries.clone(), - l2, - t: self.params.config.t, - n, - }, - inputs: ProximityVerifierInputs { - fresh: &fresh_handle, - acc: &acc_handles, - _f: PhantomData, - }, - )?; - - // ── Recompute ν vector from shift answers ─────────────────── - let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma); - let mut nus = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); - nus.push(nu_0); - nus.extend(answers); - for v_jk in proof.shift_query_answers.iter() { - let nu_st = v_jk - .iter() - .zip(&gamma_eq_evals) - .fold(F::zero(), |acc, (v, eq)| acc + *eq * *v); - nus.push(nu_st); - } - - // ── IOR 6: Batching::verify ────────────────────────────────── - let IorVerifyResult { - reduced: BatchingReducedStatement { alpha }, - outputs: _, - } = verify_ior!( - batching_ior, - verifier_state, - statement: BatchingStatement::from_ior_outputs( - zeta_0.clone(), - &samples_flat, - &queries.evaluation_points, - self.params.config.s, - self.params.config.t, - log_n, - ), - inputs: BatchingVerifierInputs { - nus, - acc_mu: acc_mu_first, - }, - )?; - - // ── Post-IOR accumulator consistency ─────────────────────── - (acc_alpha_first == alpha) - .then_some(()) - .ok_or(VerifierError::CodeEvaluationPoint)?; - - let betas = l2_taus - .into_iter() - .chain(l1_taus) - .zip(l2_xs.into_iter().chain(l1_xs)) - .map(|(tau_i, x)| concat_slices(&tau_i, &x)) - .collect::>>(); - let beta = scale_and_sum(&betas, &gamma_eq_evals); - let expected_beta = concat_slices(&acc_beta_0_first, &acc_beta_1_first); - (expected_beta == beta) - .then_some(()) - .ok_or(VerifierError::CircuitEvaluationPoint)?; - - Ok(()) - } - - fn decide( - &self, - acc_witness: AccumulatorWitness, - acc_instance: AccumulatorInstance, - ) -> Result<(), WARPError> { - let acc_codeword = &acc_witness.td[0].codewords()[0]; - - let computed_f = self.params.code.encode(&acc_witness.w[0]); - (acc_codeword == &computed_f).then_some(()).ok_or(DeciderError::EncodedWitness)?; - - let scheme = warp_scheme::(self.params.hasher.clone(), self.params.code.code_len()); - let recomputed = scheme.commit(std::slice::from_ref(&computed_f)); - (acc_instance.rt[0] == *recomputed.root()).then_some(()).ok_or(DeciderError::MerkleRoot)?; - (acc_witness.td[0].root() == recomputed.root()).then_some(()).ok_or(DeciderError::MerkleTrapDoor)?; - - let f_hat = DenseMultilinearExtension::from_evaluations_slice( - log2(self.params.code.code_len()) as usize, - acc_codeword, - ); - (f_hat.evaluate(&acc_instance.alpha[0]) == acc_instance.mu[0]) - .then_some(()).ok_or(DeciderError::MLExtensionEvaluation)?; - - let tau = &acc_instance.beta.0[0]; - let tau_zero_evader = compute_hypercube_eq_evals(tau.len(), tau); - let mut z = acc_instance.beta.1[0].clone(); - z.extend(acc_witness.w[0].clone()); - let computed_eta = self - .params - .p - .evaluate_bundled(&tau_zero_evader, &z) - .unwrap(); - (computed_eta == acc_instance.eta[0]).then_some(()).ok_or(DeciderError::BundledEvaluation)?; - - Ok(()) - } -} +pub use crate::warp::WARP; diff --git a/src/protocol/ior.rs b/src/protocol/ior.rs new file mode 100644 index 0000000..dc09e98 --- /dev/null +++ b/src/protocol/ior.rs @@ -0,0 +1,162 @@ +//! The Interactive Oracle Reduction abstraction. Paired spec: +//! `docs/paper-mods/mod1_oracle.tex`. + +#![allow(clippy::type_complexity)] + +use spongefish::{ProverState, VerifierState}; + +use crate::error::{ProverError, VerifierError}; + +/// Interactive Oracle Reduction. `(stmt, wit, oracles_in) -> (stmt', +/// oracles_out)`. Implementors write `prove_inner` / `verify_inner` / +/// `reduce_statement`; the default `prove` / `verify` chain them so the +/// reduced statement is computed in one place and prover/verifier +/// cannot drift. +pub trait IOR { + const NAME: &'static str; + + type Statement<'a> + where + Self: 'a; + type Witness<'a> + where + Self: 'a; + type ProverInputs<'a> + where + Self: 'a; + type VerifierInputs<'a> + where + Self: 'a; + type ReductionInputs; + type ReducedStatement; + type ProofString; + type ReducedWitness; + type VerifierOutputs; + + /// Single source of truth: both sides feed their `ReductionInputs` here. + fn reduce_statement<'a>( + &self, + statement: &Self::Statement<'a>, + inputs: &Self::ReductionInputs, + ) -> Self::ReducedStatement + where + Self: 'a; + + fn prove_inner<'a>( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement<'a>, + witness: &Self::Witness<'a>, + inputs: &Self::ProverInputs<'a>, + ) -> Result< + ( + Self::ReductionInputs, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > + where + Self: 'a; + + fn verify_inner<'a, 'b>( + &self, + verifier_state: &mut VerifierState<'a>, + statement: &Self::Statement<'b>, + inputs: &Self::VerifierInputs<'b>, + ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> + where + Self: 'b; + + fn prove<'a>( + &self, + prover_state: &mut ProverState, + statement: &Self::Statement<'a>, + witness: &Self::Witness<'a>, + inputs: &Self::ProverInputs<'a>, + ) -> Result< + ( + Self::ReducedStatement, + Self::ProofString, + Self::ReducedWitness, + ), + ProverError, + > + where + Self: 'a, + { + let (red_inputs, proof, red_wit) = + self.prove_inner(prover_state, statement, witness, inputs)?; + let reduced = self.reduce_statement(statement, &red_inputs); + Ok((reduced, proof, red_wit)) + } + + fn verify<'a, 'b>( + &self, + verifier_state: &mut VerifierState<'a>, + statement: &Self::Statement<'b>, + inputs: &Self::VerifierInputs<'b>, + ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> + where + Self: 'b, + { + let (red_inputs, vouts) = self.verify_inner(verifier_state, statement, inputs)?; + let reduced = self.reduce_statement(statement, &red_inputs); + Ok((reduced, vouts)) + } +} + +/// Destructuring carrier for [`IOR::prove`]. +pub struct IorProveResult { + pub reduced: R, + pub proof: P, + pub witness: W, +} + +/// Destructuring carrier for [`IOR::verify`]. +pub struct IorVerifyResult { + pub reduced: R, + pub outputs: V, +} + +/// Call `IOR::prove` and package the `(reduced, proof, witness)` tuple +/// into a named-field [`IorProveResult`]. +#[macro_export] +macro_rules! prove_ior { + ( + $ior:expr, + $transcript:expr, + statement: $statement:expr, + witness: $witness:expr, + inputs: $inputs:expr $(,)? + ) => {{ + let __stmt = $statement; + let __wit = $witness; + let __ins = $inputs; + $crate::protocol::ior::IOR::prove(&$ior, $transcript, &__stmt, &__wit, &__ins).map( + |(reduced, proof, witness)| $crate::protocol::ior::IorProveResult { + reduced, + proof, + witness, + }, + ) + }}; +} + +/// Call `IOR::verify` and package the `(reduced, outputs)` tuple into a +/// named-field [`IorVerifyResult`]. +#[macro_export] +macro_rules! verify_ior { + ( + $ior:expr, + $transcript:expr, + statement: $statement:expr, + inputs: $inputs:expr $(,)? + ) => {{ + let __stmt = $statement; + let __ins = $inputs; + $crate::protocol::ior::IOR::verify(&$ior, $transcript, &__stmt, &__ins).map( + |(reduced, outputs)| $crate::protocol::ior::IorVerifyResult { reduced, outputs }, + ) + }}; +} diff --git a/src/protocol/iors/batching.rs b/src/protocol/iors/batching.rs index c29810f..bf024e1 100644 --- a/src/protocol/iors/batching.rs +++ b/src/protocol/iors/batching.rs @@ -37,8 +37,8 @@ use std::marker::PhantomData; use crate::count_ops; use crate::error::{ProverError, VerifierError}; -use crate::protocol::oracle::Oracle; -use crate::protocol::iors::IOR; +use crate::protocol::oracles::evaluation::Oracle; +use crate::protocol::ior::IOR; use crate::protocol::transcript::EffscVerifierTranscript; use crate::utils::poly::{eq_poly, eq_poly_non_binary}; diff --git a/src/protocol/iors/bridge.rs b/src/protocol/iors/bridge.rs index bda2536..452cd26 100644 --- a/src/protocol/iors/bridge.rs +++ b/src/protocol/iors/bridge.rs @@ -45,9 +45,9 @@ use std::marker::PhantomData; use crate::count_ops; use crate::crypto::merkle::{warp_scheme, WarpCommitted}; use crate::error::{ProverError, VerifierError}; -use crate::protocol::oracle::Oracle; +use crate::protocol::oracles::evaluation::Oracle; use crate::protocol::iors::twin_constraint::DeferredOracleCheck; -use crate::protocol::iors::IOR; +use crate::protocol::ior::IOR; use crate::relations::BundledPESAT; pub struct BridgeStatement { diff --git a/src/protocol/iors/mod.rs b/src/protocol/iors/mod.rs index 00a1f1b..45e191c 100644 --- a/src/protocol/iors/mod.rs +++ b/src/protocol/iors/mod.rs @@ -1,200 +1,9 @@ -//! Warp's Interactive Oracle Reductions. Paired spec: -//! `docs/paper-mods/mod1_oracle.tex`. +//! Concrete Warp IORs, in protocol order. -#![allow(clippy::type_complexity)] - -pub mod batching; +pub mod pesat; +pub mod twin_constraint; pub mod bridge; pub mod ood; -pub mod oracle_handle; -pub mod pesat; -pub mod proximity; pub mod sample_queries; -pub mod twin_constraint; - -use spongefish::{ProverState, VerifierState}; - -use crate::error::{ProverError, VerifierError}; - -/// Interactive Oracle Reduction. `(stmt, wit, oracles_in) -> (stmt', -/// oracles_out)`. Implementors write `prove_inner` / `verify_inner` / -/// `reduce_statement`; the default `prove` / `verify` chain them so the -/// reduced statement is computed in one place and prover/verifier -/// cannot drift. -pub trait IOR { - const NAME: &'static str; - - type Statement<'a> - where - Self: 'a; - type Witness<'a> - where - Self: 'a; - type ProverInputs<'a> - where - Self: 'a; - type VerifierInputs<'a> - where - Self: 'a; - type ReductionInputs; - type ReducedStatement; - type ProofString; - type ReducedWitness; - type VerifierOutputs; - - /// Single source of truth: both sides feed their `ReductionInputs` here. - fn reduce_statement<'a>( - &self, - statement: &Self::Statement<'a>, - inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement - where - Self: 'a; - - /// Implementor's prover-side body. Runs the protocol's prover - /// machinery and returns the inputs `reduce_statement` needs, plus - /// the proof string and reduced witness. - fn prove_inner<'a>( - &self, - prover_state: &mut ProverState, - statement: &Self::Statement<'a>, - witness: &Self::Witness<'a>, - inputs: &Self::ProverInputs<'a>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > - where - Self: 'a; - - /// Implementor's verifier-side body. Runs the protocol's verifier - /// machinery (sumcheck checks, transcript reads, soundness checks) - /// and returns the inputs `reduce_statement` needs. - fn verify_inner<'a, 'b>( - &self, - verifier_state: &mut VerifierState<'a>, - statement: &Self::Statement<'b>, - inputs: &Self::VerifierInputs<'b>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> - where - Self: 'b; - - /// Default impl. Implementors should not override. - fn prove<'a>( - &self, - prover_state: &mut ProverState, - statement: &Self::Statement<'a>, - witness: &Self::Witness<'a>, - inputs: &Self::ProverInputs<'a>, - ) -> Result< - ( - Self::ReducedStatement, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > - where - Self: 'a, - { - let (red_inputs, proof, red_wit) = - self.prove_inner(prover_state, statement, witness, inputs)?; - let reduced = self.reduce_statement(statement, &red_inputs); - Ok((reduced, proof, red_wit)) - } - - /// Default impl. Implementors should not override. - fn verify<'a, 'b>( - &self, - verifier_state: &mut VerifierState<'a>, - statement: &Self::Statement<'b>, - inputs: &Self::VerifierInputs<'b>, - ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> - where - Self: 'b, - { - let (red_inputs, vouts) = self.verify_inner(verifier_state, statement, inputs)?; - let reduced = self.reduce_statement(statement, &red_inputs); - Ok((reduced, vouts)) - } -} - -// ─── Choreography-syntax helpers ───────────────────────────────────────── -// -// Call-site sugar: the macros wrap the trait's tuple return into the -// named-field structs below so `lib.rs` can destructure by name. - -/// Generic destructuring carrier for `IOR::prove`. The macro -/// [`prove_ior!`] returns this so callers can write -/// `let IorProveResult { reduced, proof, witness } = prove_ior!(...)?;`. -pub struct IorProveResult { - pub reduced: R, - pub proof: P, - pub witness: W, -} - -/// Generic destructuring carrier for `IOR::verify`. -pub struct IorVerifyResult { - pub reduced: R, - pub outputs: V, -} - -/// Calls `IOR::prove` on the given IOR and packages the `(reduced, -/// proof, witness)` tuple into a named-field [`IorProveResult`]. -/// -/// ```ignore -/// let IorProveResult { -/// reduced: PesatReducedStatement { mus, taus }, -/// proof: _, -/// witness: PesatReducedWitness { codewords, td_0 }, -/// } = prove_ior!( -/// pesat_ior, -/// prover_state, -/// statement: PesatStatement { l1, log_m }, -/// witness: PesatWitness { witnesses: &witnesses }, -/// inputs: (), -/// )?; -/// ``` -#[macro_export] -macro_rules! prove_ior { - ( - $ior:expr, - $transcript:expr, - statement: $statement:expr, - witness: $witness:expr, - inputs: $inputs:expr $(,)? - ) => {{ - let __stmt = $statement; - let __wit = $witness; - let __ins = $inputs; - $crate::protocol::iors::IOR::prove(&$ior, $transcript, &__stmt, &__wit, &__ins).map( - |(reduced, proof, witness)| $crate::protocol::iors::IorProveResult { - reduced, - proof, - witness, - }, - ) - }}; -} - -/// Calls `IOR::verify` on the given IOR and packages the `(reduced, -/// outputs)` tuple into a named-field [`IorVerifyResult`]. -#[macro_export] -macro_rules! verify_ior { - ( - $ior:expr, - $transcript:expr, - statement: $statement:expr, - inputs: $inputs:expr $(,)? - ) => {{ - let __stmt = $statement; - let __ins = $inputs; - $crate::protocol::iors::IOR::verify(&$ior, $transcript, &__stmt, &__ins).map( - |(reduced, outputs)| $crate::protocol::iors::IorVerifyResult { reduced, outputs }, - ) - }}; -} +pub mod batching; +pub mod proximity; diff --git a/src/protocol/iors/ood.rs b/src/protocol/iors/ood.rs index a45466b..8d3785b 100644 --- a/src/protocol/iors/ood.rs +++ b/src/protocol/iors/ood.rs @@ -2,7 +2,7 @@ //! //! Paired spec: `docs/paper-mods/mod1_oracle.tex`. This IOR is a thin //! composition of point queries on the committed oracle — see -//! [`Oracle::query_at_point`](crate::protocol::oracle::Oracle::query_at_point). +//! [`Oracle::query_at_point`](crate::protocol::oracles::evaluation::Oracle::query_at_point). //! The verifier derives the same random points from the transcript. //! //! IOR signature @@ -25,8 +25,8 @@ use std::marker::PhantomData; use crate::count_ops; use crate::error::{ProverError, VerifierError}; -use crate::protocol::oracle::Oracle; -use crate::protocol::iors::IOR; +use crate::protocol::oracles::evaluation::Oracle; +use crate::protocol::ior::IOR; pub struct OodStatement { pub s: usize, diff --git a/src/protocol/iors/pesat.rs b/src/protocol/iors/pesat.rs index 2b25d95..deb516a 100644 --- a/src/protocol/iors/pesat.rs +++ b/src/protocol/iors/pesat.rs @@ -25,7 +25,7 @@ use std::marker::PhantomData; use crate::count_ops; use crate::crypto::merkle::{encode_codewords, warp_scheme, WarpCommitted}; use crate::error::{ProverError, VerifierError}; -use crate::protocol::iors::IOR; +use crate::protocol::ior::IOR; pub struct PesatStatement { pub l1: usize, diff --git a/src/protocol/iors/proximity.rs b/src/protocol/iors/proximity.rs index 84913fc..0cd19ff 100644 --- a/src/protocol/iors/proximity.rs +++ b/src/protocol/iors/proximity.rs @@ -26,9 +26,9 @@ use std::marker::PhantomData; use crate::count_ops; use crate::crypto::merkle::{warp_scheme, WarpCommitted, WarpProof}; use crate::error::{ProverError, VerifierError}; -use crate::protocol::iors::oracle_handle::IndexedOracle; -use crate::protocol::iors::IOR; -use crate::protocol::query::QueryIndices; +use crate::protocol::oracles::indexed_merkle::IndexedOracle; +use crate::protocol::ior::IOR; +use crate::protocol::oracles::query_indices::QueryIndices; pub struct ProximityStatement { pub queries: QueryIndices, @@ -48,16 +48,8 @@ where pub acc_td: &'a [WarpCommitted], } -/// Verifier-side inputs for Proximity. -/// -/// The IOR verifier no longer sees roots / opening proofs / answer -/// tables directly; it sees [`IndexedOracle`] handles and triggers -/// their (lazy, memoized) BCS validation. IORs stay BCS-agnostic at -/// the type level — the orchestrator picks the concrete handle type -/// (today: [`oracle_handle::MerkleIndexedOracle`]). -/// -/// Generic over `O: IndexedOracle>` so dispatch is static; no -/// trait objects. +/// Verifier-side inputs. The IOR sees [`IndexedOracle`] handles, not +/// raw roots / opening proofs — IORs stay BCS-agnostic. pub struct ProximityVerifierInputs<'a, F, O> where F: Field, @@ -118,7 +110,7 @@ where = ProximityVerifierInputs< 'b, F, - crate::protocol::iors::oracle_handle::MerkleIndexedOracle<'b, F, H>, + crate::protocol::oracles::indexed_merkle::MerkleIndexedOracle<'b, F, H>, > where Self: 'b; diff --git a/src/protocol/iors/sample_queries.rs b/src/protocol/iors/sample_queries.rs index 24685ea..c1498b3 100644 --- a/src/protocol/iors/sample_queries.rs +++ b/src/protocol/iors/sample_queries.rs @@ -25,8 +25,8 @@ use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState use std::marker::PhantomData; use crate::error::{ProverError, VerifierError}; -use crate::protocol::iors::IOR; -use crate::protocol::query::QueryIndices; +use crate::protocol::ior::IOR; +use crate::protocol::oracles::query_indices::QueryIndices; pub struct SampleQueriesStatement { pub log_n: usize, diff --git a/src/protocol/iors/twin_constraint.rs b/src/protocol/iors/twin_constraint.rs index dd31840..c0b5967 100644 --- a/src/protocol/iors/twin_constraint.rs +++ b/src/protocol/iors/twin_constraint.rs @@ -48,11 +48,11 @@ use std::marker::PhantomData; use crate::count_ops; use crate::error::{ProverError, VerifierError}; -use crate::protocol::oracle::Oracle; -use crate::protocol::iors::IOR; +use crate::protocol::oracles::evaluation::Oracle; +use crate::protocol::ior::IOR; use crate::protocol::transcript::EffscVerifierTranscript; use crate::relations::r1cs::R1CSConstraints; -use crate::types::AccumulatorInstance; +use crate::warp::AccumulatorInstance; use crate::utils::{ concat_slices, poly::{eq_poly, eq_poly_non_binary}, diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 9d93709..1c195b9 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -1,4 +1,4 @@ -pub mod oracle; +pub mod ior; pub mod iors; -pub mod query; +pub mod oracles; pub mod transcript; diff --git a/src/protocol/oracle.rs b/src/protocol/oracles/evaluation.rs similarity index 100% rename from src/protocol/oracle.rs rename to src/protocol/oracles/evaluation.rs diff --git a/src/protocol/iors/oracle_handle.rs b/src/protocol/oracles/indexed_merkle.rs similarity index 100% rename from src/protocol/iors/oracle_handle.rs rename to src/protocol/oracles/indexed_merkle.rs diff --git a/src/protocol/oracles/mod.rs b/src/protocol/oracles/mod.rs new file mode 100644 index 0000000..914d31e --- /dev/null +++ b/src/protocol/oracles/mod.rs @@ -0,0 +1,6 @@ +//! Oracle vocabulary used by Warp's IORs: codeword evaluation oracles, +//! indexed-oracle handles, and verifier query positions. + +pub mod evaluation; +pub mod indexed_merkle; +pub mod query_indices; diff --git a/src/protocol/query.rs b/src/protocol/oracles/query_indices.rs similarity index 100% rename from src/protocol/query.rs rename to src/protocol/oracles/query_indices.rs diff --git a/src/protocol/transcript/prover.rs b/src/protocol/transcript/prover.rs index b6f5442..476ffc3 100644 --- a/src/protocol/transcript/prover.rs +++ b/src/protocol/transcript/prover.rs @@ -2,7 +2,7 @@ use ark_ff::Field; use ark_mt::MerkleHasher; use spongefish::{Encoding, ProverState}; -use crate::types::AccumulatorInstance; +use crate::warp::AccumulatorInstance; // absorb a list of plain instances into the transcript pub fn absorb_instances>( diff --git a/src/protocol/transcript/verifier.rs b/src/protocol/transcript/verifier.rs index 0bea026..445a56f 100644 --- a/src/protocol/transcript/verifier.rs +++ b/src/protocol/transcript/verifier.rs @@ -2,7 +2,7 @@ use ark_ff::Field; use ark_mt::MerkleHasher; use spongefish::{Decoding, Encoding, NargDeserialize, VerificationResult, VerifierState}; -use crate::types::AccumulatorInstance; +use crate::warp::AccumulatorInstance; // (l1 instances, accumulated instance) pub type ParsedStatement = (Vec>, AccumulatorInstance); diff --git a/src/serialize.rs b/src/serialize.rs index d72e0ea..7b5e31a 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -3,7 +3,7 @@ use ark_mt::MerkleHasher; use ark_serialize::{CanonicalSerialize, Compress, SerializationError, Valid, Write}; use crate::crypto::merkle::WarpProof; -use crate::types::{AccumulatorInstance, AccumulatorWitness, WARPProof}; +use crate::warp::{AccumulatorInstance, AccumulatorWitness, WARPProof}; // `AccumulatorInstance` and `WARPProof` carry generic associated types // (`H::Digest`, `WarpProof`) whose serializability is not implied by diff --git a/src/types.rs b/src/types.rs deleted file mode 100644 index 3713e59..0000000 --- a/src/types.rs +++ /dev/null @@ -1,197 +0,0 @@ -use ark_codes::traits::LinearCode; -use ark_ff::Field; -use ark_mt::MerkleHasher; -use std::marker::PhantomData; - -use crate::config::WARPConfig; -use crate::crypto::merkle::{WarpCommitted, WarpProof}; -use crate::error::ProverError; -use crate::relations::BundledPESAT; - -/// Prover key — the relation index plus dimensions `(M, N, k)`. -#[derive(Clone)] -pub struct WARPProverKey

    { - pub index: P, - pub m: usize, - pub n: usize, - pub k: usize, -} - -/// Verifier key — dimensions only `(M, N, k)`. -#[derive(Clone, Copy)] -pub struct WARPVerifierKey { - pub m: usize, - pub n: usize, - pub k: usize, -} - -// result of a prove call: (new accumulator instance + witness, proof) -pub type ProveResult = Result< - ( - (AccumulatorInstance, AccumulatorWitness), - WARPProof, - ), - ProverError, ->; - -/// Protocol parameters for WARP — the shared configuration used by all IORs. -pub struct WARPParams, C: LinearCode + Clone, H: MerkleHasher> { - pub(crate) _f: PhantomData, - pub config: WARPConfig, - pub code: C, - pub p: P, - /// The hasher value (replaces the old leaf-hash + two-to-one-hash parameter pair). - pub hasher: H, -} - -/// Accumulator instance — the public part of an accumulated claim. -/// -/// Corresponds to `(rt, α, μ, (τ, x), η)` in the paper. -#[derive(Clone)] -pub struct AccumulatorInstance { - /// Merkle tree root commitments. - pub rt: Vec, - /// Code evaluation points (one per accumulated oracle). - pub alpha: Vec>, - /// Code evaluation targets (one per accumulated oracle). - pub mu: Vec, - /// Circuit evaluation points: `(τ_i, x_i)` pairs. - pub beta: (Vec>, Vec>), - /// Bundled PESAT evaluation targets. - pub eta: Vec, -} - -impl AccumulatorInstance { - pub fn empty() -> Self { - Self { - rt: vec![], - alpha: vec![], - mu: vec![], - beta: (vec![], vec![]), - eta: vec![], - } - } - - /// Number of accumulated entries. - pub fn len(&self) -> usize { - self.rt.len() - } - - pub fn is_empty(&self) -> bool { - self.rt.is_empty() - } - - /// Builder-style merge: append every field of `other` to `self`. Used - /// by callers that fold per-round single-entry outputs into a - /// multi-entry input for the next round. - pub fn extend(mut self, other: Self) -> Self { - self.rt.extend(other.rt); - self.alpha.extend(other.alpha); - self.mu.extend(other.mu); - self.beta.0.extend(other.beta.0); - self.beta.1.extend(other.beta.1); - self.eta.extend(other.eta); - self - } - -} - -/// Accumulator witness — the private part of an accumulated claim. -/// -/// Corresponds to `(td, w)` in the paper. The codewords are stored -/// inside `td[i].codewords()` so we don't carry a parallel `f` field. -pub struct AccumulatorWitness -where - F: Field, - H: MerkleHasher>, -{ - /// Committed multi-vector trees (with the codewords cached inside). - pub td: Vec>, - /// R1CS witnesses. - pub w: Vec>, -} - -impl Clone for AccumulatorWitness -where - F: Field, - H: MerkleHasher>, - WarpCommitted: Clone, -{ - fn clone(&self) -> Self { - Self { - td: self.td.clone(), - w: self.w.clone(), - } - } -} - -impl AccumulatorWitness -where - F: Field, - H: MerkleHasher>, -{ - pub fn empty() -> Self { - Self { - td: vec![], - w: vec![], - } - } - - pub fn len(&self) -> usize { - self.td.len() - } - - pub fn is_empty(&self) -> bool { - self.td.is_empty() - } - - /// Builder-style merge — counterpart of [`AccumulatorInstance::extend`]. - pub fn extend(mut self, other: Self) -> Self { - self.td.extend(other.td); - self.w.extend(other.w); - self - } -} - -/// Proof produced by the WARP accumulation prover. -/// -/// Corresponds to `(rt₀, μᵢ, ν₀, νᵢ, auth₀, authⱼ, f_i(x_j))` in the paper. -pub struct WARPProof { - /// Fresh Merkle tree root. - pub rt_0: H::Digest, - /// Fresh code evaluations at 0. - pub mu_i: Vec, - /// Evaluation of accumulated oracle at zeta_0. - pub nu_0: F, - /// Evaluation claims (OOD + shift query answers). - pub nu_i: Vec, - /// Single multi-opening proof for the fresh PESAT commitment, covering - /// all queried indices across all `l1` interleaved codewords. - pub auth_0: WarpProof, - /// One multi-opening proof per accumulated oracle (each is a single - /// codeword tree, opened at the same `t` query positions). - pub auth_j: Vec>, - /// Shift query answers: `f_i(x_j)` for each query position `j` and oracle `i`. - pub shift_query_answers: Vec>, -} - -impl Clone for WARPProof -where - F: Field, - H: MerkleHasher, - H::Digest: Clone, - WarpProof: Clone, -{ - fn clone(&self) -> Self { - Self { - rt_0: self.rt_0.clone(), - mu_i: self.mu_i.clone(), - nu_0: self.nu_0, - nu_i: self.nu_i.clone(), - auth_0: self.auth_0.clone(), - auth_j: self.auth_j.clone(), - shift_query_answers: self.shift_query_answers.clone(), - } - } -} - diff --git a/src/warp/accumulator.rs b/src/warp/accumulator.rs new file mode 100644 index 0000000..8a430a5 --- /dev/null +++ b/src/warp/accumulator.rs @@ -0,0 +1,96 @@ +use ark_ff::Field; +use ark_mt::MerkleHasher; + +use crate::crypto::merkle::WarpCommitted; + +/// Public part of an accumulated claim: `(rt, α, μ, (τ, x), η)` in the paper. +#[derive(Clone)] +pub struct AccumulatorInstance { + pub rt: Vec, + pub alpha: Vec>, + pub mu: Vec, + pub beta: (Vec>, Vec>), + pub eta: Vec, +} + +impl AccumulatorInstance { + pub fn empty() -> Self { + Self { + rt: vec![], + alpha: vec![], + mu: vec![], + beta: (vec![], vec![]), + eta: vec![], + } + } + + pub fn len(&self) -> usize { + self.rt.len() + } + + pub fn is_empty(&self) -> bool { + self.rt.is_empty() + } + + pub fn extend(mut self, other: Self) -> Self { + self.rt.extend(other.rt); + self.alpha.extend(other.alpha); + self.mu.extend(other.mu); + self.beta.0.extend(other.beta.0); + self.beta.1.extend(other.beta.1); + self.eta.extend(other.eta); + self + } +} + +/// Private part of an accumulated claim: `(td, w)` in the paper. +/// Codewords live inside `td[i].codewords()`. +pub struct AccumulatorWitness +where + F: Field, + H: MerkleHasher>, +{ + pub td: Vec>, + pub w: Vec>, +} + +impl Clone for AccumulatorWitness +where + F: Field, + H: MerkleHasher>, + WarpCommitted: Clone, +{ + fn clone(&self) -> Self { + Self { + td: self.td.clone(), + w: self.w.clone(), + } + } +} + +impl AccumulatorWitness +where + F: Field, + H: MerkleHasher>, +{ + pub fn empty() -> Self { + Self { + td: vec![], + w: vec![], + } + } + + pub fn len(&self) -> usize { + self.td.len() + } + + pub fn is_empty(&self) -> bool { + self.td.is_empty() + } + + pub fn extend(mut self, other: Self) -> Self { + self.td.extend(other.td); + self.w.extend(other.w); + self + } +} diff --git a/src/warp/decide.rs b/src/warp/decide.rs new file mode 100644 index 0000000..43f213e --- /dev/null +++ b/src/warp/decide.rs @@ -0,0 +1,67 @@ +use ark_codes::traits::LinearCode; +use ark_ff::{Field, PrimeField}; +use ark_mt::MerkleHasher; +use ark_poly::{DenseMultilinearExtension, Polynomial}; +use ark_std::log2; +use effsc::hypercube::compute_hypercube_eq_evals; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize}; + +use crate::crypto::merkle::warp_scheme; +use crate::error::{DeciderError, WARPError}; +use crate::relations::{r1cs::R1CSConstraints, BundledPESAT}; +use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; +use crate::warp::scheme::WARP; + +impl WARP +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + P: Clone + BundledPESAT, Config = (usize, usize, usize)>, + C: LinearCode + Clone, + H: MerkleHasher>, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, +{ + pub(crate) fn decide_impl( + &self, + acc_witness: AccumulatorWitness, + acc_instance: AccumulatorInstance, + ) -> Result<(), WARPError> { + let acc_codeword = &acc_witness.td[0].codewords()[0]; + + let computed_f = self.params.code.encode(&acc_witness.w[0]); + (acc_codeword == &computed_f) + .then_some(()) + .ok_or(DeciderError::EncodedWitness)?; + + let scheme = warp_scheme::(self.params.hasher.clone(), self.params.code.code_len()); + let recomputed = scheme.commit(std::slice::from_ref(&computed_f)); + (acc_instance.rt[0] == *recomputed.root()) + .then_some(()) + .ok_or(DeciderError::MerkleRoot)?; + (acc_witness.td[0].root() == recomputed.root()) + .then_some(()) + .ok_or(DeciderError::MerkleTrapDoor)?; + + let f_hat = DenseMultilinearExtension::from_evaluations_slice( + log2(self.params.code.code_len()) as usize, + acc_codeword, + ); + (f_hat.evaluate(&acc_instance.alpha[0]) == acc_instance.mu[0]) + .then_some(()) + .ok_or(DeciderError::MLExtensionEvaluation)?; + + let tau = &acc_instance.beta.0[0]; + let tau_zero_evader = compute_hypercube_eq_evals(tau.len(), tau); + let mut z = acc_instance.beta.1[0].clone(); + z.extend(acc_witness.w[0].clone()); + let computed_eta = self + .params + .p + .evaluate_bundled(&tau_zero_evader, &z) + .unwrap(); + (computed_eta == acc_instance.eta[0]) + .then_some(()) + .ok_or(DeciderError::BundledEvaluation)?; + + Ok(()) + } +} diff --git a/src/warp/keys.rs b/src/warp/keys.rs new file mode 100644 index 0000000..bddc1c3 --- /dev/null +++ b/src/warp/keys.rs @@ -0,0 +1,16 @@ +/// Prover key — relation index plus dimensions `(M, N, k)`. +#[derive(Clone)] +pub struct WARPProverKey

    { + pub index: P, + pub m: usize, + pub n: usize, + pub k: usize, +} + +/// Verifier key — dimensions only `(M, N, k)`. +#[derive(Clone, Copy)] +pub struct WARPVerifierKey { + pub m: usize, + pub n: usize, + pub k: usize, +} diff --git a/src/warp/mod.rs b/src/warp/mod.rs new file mode 100644 index 0000000..09f535f --- /dev/null +++ b/src/warp/mod.rs @@ -0,0 +1,16 @@ +//! WARP accumulation scheme: the choreography that composes Warp's IORs. + +pub mod accumulator; +pub mod decide; +pub mod keys; +pub mod params; +pub mod proof; +pub mod prove; +pub mod scheme; +pub mod verify; + +pub use accumulator::{AccumulatorInstance, AccumulatorWitness}; +pub use keys::{WARPProverKey, WARPVerifierKey}; +pub use params::WARPParams; +pub use proof::{ProveResult, WARPProof}; +pub use scheme::WARP; diff --git a/src/warp/params.rs b/src/warp/params.rs new file mode 100644 index 0000000..f2347b4 --- /dev/null +++ b/src/warp/params.rs @@ -0,0 +1,16 @@ +use ark_codes::traits::LinearCode; +use ark_ff::Field; +use ark_mt::MerkleHasher; +use std::marker::PhantomData; + +use crate::config::WARPConfig; +use crate::relations::BundledPESAT; + +/// Shared configuration used by all IORs. +pub struct WARPParams, C: LinearCode + Clone, H: MerkleHasher> { + pub(crate) _f: PhantomData, + pub config: WARPConfig, + pub code: C, + pub p: P, + pub hasher: H, +} diff --git a/src/warp/proof.rs b/src/warp/proof.rs new file mode 100644 index 0000000..a859649 --- /dev/null +++ b/src/warp/proof.rs @@ -0,0 +1,46 @@ +use ark_ff::Field; +use ark_mt::MerkleHasher; + +use crate::crypto::merkle::WarpProof; +use crate::error::ProverError; +use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; + +/// Proof produced by the WARP accumulation prover. Corresponds to +/// `(rt₀, μᵢ, ν₀, νᵢ, auth₀, authⱼ, f_i(x_j))` in the paper. +pub struct WARPProof { + pub rt_0: H::Digest, + pub mu_i: Vec, + pub nu_0: F, + pub nu_i: Vec, + pub auth_0: WarpProof, + pub auth_j: Vec>, + pub shift_query_answers: Vec>, +} + +impl Clone for WARPProof +where + F: Field, + H: MerkleHasher, + H::Digest: Clone, + WarpProof: Clone, +{ + fn clone(&self) -> Self { + Self { + rt_0: self.rt_0.clone(), + mu_i: self.mu_i.clone(), + nu_0: self.nu_0, + nu_i: self.nu_i.clone(), + auth_0: self.auth_0.clone(), + auth_j: self.auth_j.clone(), + shift_query_answers: self.shift_query_answers.clone(), + } + } +} + +pub type ProveResult = Result< + ( + (AccumulatorInstance, AccumulatorWitness), + WARPProof, + ), + ProverError, +>; diff --git a/src/warp/prove.rs b/src/warp/prove.rs new file mode 100644 index 0000000..ee08c15 --- /dev/null +++ b/src/warp/prove.rs @@ -0,0 +1,289 @@ +use ark_codes::traits::LinearCode; +use ark_ff::{Field, PrimeField}; +use ark_mt::MerkleHasher; +use ark_std::log2; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; +use std::marker::PhantomData; + +use crate::error::ProverError; +use crate::prove_ior; +use crate::protocol::ior::IorProveResult; +use crate::protocol::iors::{ + batching::{ + Batching, BatchingProverInputs, BatchingReducedStatement, BatchingReducedWitness, + BatchingStatement, + }, + bridge::{ + Bridge, BridgeProverInputs, BridgeReducedStatement, BridgeReducedWitness, BridgeStatement, + BridgeWitness, + }, + ood::{Ood, OodProverInputs, OodReducedStatement, OodStatement}, + pesat::{Pesat, PesatReducedStatement, PesatReducedWitness, PesatStatement, PesatWitness}, + proximity::{Proximity, ProximityProofString, ProximityProverInputs, ProximityStatement}, + sample_queries::{SampleQueries, SampleQueriesReducedStatement, SampleQueriesStatement}, + twin_constraint::{ + TwinConstraint, TwinConstraintProverInputs, TwinConstraintReducedStatement, + TwinConstraintReducedWitness, TwinConstraintStatement, TwinConstraintWitness, + }, +}; +use crate::protocol::transcript::absorb_instances; +use crate::relations::{r1cs::R1CSConstraints, BundledPESAT}; +use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; +use crate::warp::keys::WARPProverKey; +use crate::warp::proof::{ProveResult, WARPProof}; +use crate::warp::scheme::WARP; + +impl WARP +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + P: Clone + BundledPESAT, Config = (usize, usize, usize)>, + C: LinearCode + Clone, + H: MerkleHasher>, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, +{ + #[tracing::instrument(name = "warp.prove", skip_all)] + pub(crate) fn prove_impl( + &self, + pk: WARPProverKey

    , + prover_state: &mut ProverState, + witnesses: Vec>, + instances: Vec>, + acc_instance: AccumulatorInstance, + acc_witness: AccumulatorWitness, + ) -> ProveResult { + if instances.len() < 2 { + return Err(ProverError::InsufficientInstances { + got: instances.len(), + }); + } + if witnesses.len() != instances.len() { + return Err(ProverError::InstanceWitnessLengthMismatch { + instances: instances.len(), + witnesses: witnesses.len(), + }); + } + if acc_witness.td.len() != acc_instance.rt.len() { + return Err(ProverError::AccumulatorShapeMismatch { + instances: acc_instance.rt.len(), + roots: acc_witness.td.len(), + }); + } + + let (l1, l) = (self.params.config.l1, self.params.config.l); + let l2 = l - l1; + if !l.is_power_of_two() { + return Err(ProverError::ConfigParameterInvalid { + reason: format!("config.l = {l} is not a power of two"), + }); + } + + #[allow(non_snake_case)] + let (M, N, k) = (pk.m, pk.n, pk.k); + let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); + let n = self.params.code.code_len(); + let log_n = log2(n) as usize; + + if instances[0].len() != N - k { + return Err(ProverError::InstanceLengthMismatch { + expected: N - k, + got: instances[0].len(), + }); + } + absorb_instances(prover_state, &instances); + acc_instance.absorb_into(prover_state); + + let AccumulatorWitness { + td: acc_tds, + w: acc_ws, + } = acc_witness; + let acc_fs: Vec> = acc_tds.iter().map(|td| td.codewords()[0].clone()).collect(); + + let pesat_ior = Pesat:: { + code: &self.params.code, + hasher: &self.params.hasher, + _phantom: PhantomData, + }; + let twin_constraint_ior = TwinConstraint:: { + r1cs: self.params.p.constraints(), + _phantom: PhantomData, + }; + let bridge_ior = Bridge::::new(); + let ood_ior = Ood::::new(); + let sample_queries_ior = SampleQueries::::new(); + let batching_ior = Batching::::new(); + let proximity_ior = Proximity:: { + hasher: &self.params.hasher, + _phantom: PhantomData, + }; + + // ── IOR 1: PESAT ───────────────────────────────────────────── + let IorProveResult { + reduced: PesatReducedStatement { mus, taus }, + proof: _, + witness: PesatReducedWitness { codewords, td_0 }, + } = prove_ior!( + pesat_ior, + prover_state, + statement: PesatStatement { l1, log_m }, + witness: PesatWitness { witnesses: &witnesses }, + inputs: (), + )?; + + // ── IOR 2: TwinConstraint ──────────────────────────────────── + let IorProveResult { + reduced: TwinConstraintReducedStatement { + gamma: _, + zeta_0, + beta_tau, + deferred: _, + }, + proof: _, + witness: TwinConstraintReducedWitness { f, z }, + } = prove_ior!( + twin_constraint_ior, + prover_state, + statement: TwinConstraintStatement { + acc_instance, + l1_mus: mus.clone(), + l1_taus: taus, + log_l, + log_m, + log_n, + }, + witness: TwinConstraintWitness { + acc_witness_w: &acc_ws, + instances: &instances, + witnesses: &witnesses, + }, + inputs: TwinConstraintProverInputs { + fresh_codewords: &codewords, + acc_codewords: &acc_fs, + }, + )?; + + // ── IOR 3: Bridge ──────────────────────────────────────────── + let IorProveResult { + reduced: BridgeReducedStatement { + eta, + nu_0, + td_new_root: _, + }, + proof: _, + witness: BridgeReducedWitness { td_new, new_x, new_w }, + } = prove_ior!( + bridge_ior, + prover_state, + statement: BridgeStatement { + zeta_0: zeta_0.clone(), + beta_tau: beta_tau.clone(), + log_m, + n_minus_k: N - k, + }, + witness: BridgeWitness { z: &z, f: &f }, + inputs: BridgeProverInputs { + bundled_pesat: &self.params.p, + hasher: &self.params.hasher, + code_len: n, + _f: PhantomData, + }, + )?; + + // ── IOR 4: OOD ─────────────────────────────────────────────── + let IorProveResult { + reduced: OodReducedStatement { samples_flat, answers }, + proof: _, + witness: _, + } = prove_ior!( + ood_ior, + prover_state, + statement: OodStatement { s: self.params.config.s, log_n }, + witness: (), + inputs: OodProverInputs { oracle: &f }, + )?; + + // ── IOR 5: SampleQueries ───────────────────────────────────── + let IorProveResult { + reduced: SampleQueriesReducedStatement { queries }, + proof: _, + witness: _, + } = prove_ior!( + sample_queries_ior, + prover_state, + statement: SampleQueriesStatement { log_n, t: self.params.config.t }, + witness: (), + inputs: (), + )?; + + // ── IOR 6: Batching ────────────────────────────────────────── + let IorProveResult { + reduced: BatchingReducedStatement { alpha }, + proof: _, + witness: BatchingReducedWitness { mu }, + } = prove_ior!( + batching_ior, + prover_state, + statement: BatchingStatement::from_ior_outputs( + zeta_0.clone(), + &samples_flat, + &queries.evaluation_points, + self.params.config.s, + self.params.config.t, + log_n, + ), + witness: (), + inputs: BatchingProverInputs { oracle: &f }, + )?; + + // ── IOR 7: Proximity ───────────────────────────────────────── + let IorProveResult { + reduced: _, + proof: ProximityProofString { + auth_0, + auth_j, + shift_query_answers, + }, + witness: _, + } = prove_ior!( + proximity_ior, + prover_state, + statement: ProximityStatement { + queries: queries.clone(), + l2, + t: self.params.config.t, + n, + }, + witness: (), + inputs: ProximityProverInputs { + td_0: &td_0, + acc_td: &acc_tds, + }, + )?; + + let mut nus = Vec::with_capacity(1 + self.params.config.s); + nus.push(nu_0); + nus.extend(answers); + + let new_acc_instance = AccumulatorInstance { + rt: vec![td_new.root().clone()], + alpha: vec![alpha], + mu: vec![mu], + beta: (vec![beta_tau], vec![new_x]), + eta: vec![eta], + }; + let new_acc_witness = AccumulatorWitness { + td: vec![td_new], + w: vec![new_w], + }; + let proof = WARPProof { + rt_0: td_0.root().clone(), + mu_i: mus, + nu_0, + nu_i: nus, + auth_0, + auth_j, + shift_query_answers, + }; + + Ok(((new_acc_instance, new_acc_witness), proof)) + } +} diff --git a/src/warp/scheme.rs b/src/warp/scheme.rs new file mode 100644 index 0000000..5aa16b9 --- /dev/null +++ b/src/warp/scheme.rs @@ -0,0 +1,101 @@ +use ark_codes::traits::LinearCode; +use ark_ff::{Field, PrimeField}; +use ark_mt::MerkleHasher; +use spongefish::{ + Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerificationResult, + VerifierState, +}; +use std::marker::PhantomData; + +use crate::accumulation::AccumulationScheme; +use crate::config::WARPConfig; +use crate::error::{VerifierError, WARPError}; +use crate::relations::{r1cs::R1CSConstraints, BundledPESAT}; +use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; +use crate::warp::keys::{WARPProverKey, WARPVerifierKey}; +use crate::warp::params::WARPParams; +use crate::warp::proof::{ProveResult, WARPProof}; + +pub struct WARP, C: LinearCode + Clone, H: MerkleHasher> { + pub params: WARPParams, +} + +impl WARP +where + F: Field, + P: Clone + BundledPESAT, + C: LinearCode + Clone, + H: MerkleHasher>, +{ + pub fn new(config: WARPConfig, code: C, p: P, hasher: H) -> WARP { + Self { + params: WARPParams { + _f: PhantomData, + config, + code, + p, + hasher, + }, + } + } +} + +impl AccumulationScheme for WARP +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + P: Clone + BundledPESAT, Config = (usize, usize, usize)>, + C: LinearCode + Clone, + H: MerkleHasher>, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, +{ + type Index = P; + type ProverKey = WARPProverKey

    ; + type VerifierKey = WARPVerifierKey; + type Instances = Vec>; + type Witnesses = Vec>; + + fn index( + prover_state: &mut ProverState, + index: Self::Index, + ) -> VerificationResult<(Self::ProverKey, Self::VerifierKey)> { + let (m, n, k) = index.config(); + prover_state.public_message(&index.description()); + prover_state.prover_message(&F::from(m as u32)); + prover_state.prover_message(&F::from(n as u32)); + prover_state.prover_message(&F::from(k as u32)); + Ok(( + WARPProverKey { index, m, n, k }, + WARPVerifierKey { m, n, k }, + )) + } + + fn prove( + &self, + pk: Self::ProverKey, + prover_state: &mut ProverState, + witnesses: Self::Witnesses, + instances: Self::Instances, + acc_instance: AccumulatorInstance, + acc_witness: AccumulatorWitness, + ) -> ProveResult { + self.prove_impl(pk, prover_state, witnesses, instances, acc_instance, acc_witness) + } + + fn verify<'a>( + &self, + vk: Self::VerifierKey, + verifier_state: &mut VerifierState<'a>, + acc_instance: AccumulatorInstance, + proof: WARPProof, + ) -> Result<(), VerifierError> { + self.verify_impl(vk, verifier_state, acc_instance, proof) + } + + fn decide( + &self, + acc_witness: AccumulatorWitness, + acc_instance: AccumulatorInstance, + ) -> Result<(), WARPError> { + self.decide_impl(acc_witness, acc_instance) + } +} diff --git a/src/warp/verify.rs b/src/warp/verify.rs new file mode 100644 index 0000000..36f9fa9 --- /dev/null +++ b/src/warp/verify.rs @@ -0,0 +1,283 @@ +use ark_codes::traits::LinearCode; +use ark_ff::{Field, PrimeField}; +use ark_mt::MerkleHasher; +use ark_std::log2; +use effsc::hypercube::compute_hypercube_eq_evals; +use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, VerifierState}; +use std::marker::PhantomData; + +use crate::crypto::merkle::warp_scheme; +use crate::error::VerifierError; +use crate::protocol::ior::IorVerifyResult; +use crate::protocol::iors::{ + batching::{ + Batching, BatchingReducedStatement, BatchingStatement, BatchingVerifierInputs, + }, + bridge::{Bridge, BridgeReducedStatement, BridgeStatement, BridgeVerifierInputs}, + ood::{Ood, OodReducedStatement, OodStatement}, + pesat::{Pesat, PesatReducedStatement, PesatStatement, PesatVerifierOutputs}, + proximity::{Proximity, ProximityStatement, ProximityVerifierInputs}, + sample_queries::{SampleQueries, SampleQueriesReducedStatement, SampleQueriesStatement}, + twin_constraint::{TwinConstraint, TwinConstraintReducedStatement, TwinConstraintStatement}, +}; +use crate::protocol::oracles::indexed_merkle::MerkleIndexedOracle; +use crate::protocol::transcript::parse_statement; +use crate::relations::{r1cs::R1CSConstraints, BundledPESAT}; +use crate::utils::{concat_slices, scale_and_sum}; +use crate::verify_ior; +use crate::warp::accumulator::AccumulatorInstance; +use crate::warp::keys::WARPVerifierKey; +use crate::warp::proof::WARPProof; +use crate::warp::scheme::WARP; + +impl WARP +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + P: Clone + BundledPESAT, Config = (usize, usize, usize)>, + C: LinearCode + Clone, + H: MerkleHasher>, + H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, +{ + pub(crate) fn verify_impl<'a>( + &self, + vk: WARPVerifierKey, + verifier_state: &mut VerifierState<'a>, + acc_instance: AccumulatorInstance, + proof: WARPProof, + ) -> Result<(), VerifierError> { + let (l1, l) = (self.params.config.l1, self.params.config.l); + let l2 = l - l1; + + #[allow(non_snake_case)] + let (M, N, k) = (vk.m, vk.n, vk.k); + let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); + let n = self.params.code.code_len(); + let log_n = log2(n) as usize; + + let (l1_xs, parsed_acc) = + parse_statement::(verifier_state, l1, l2, N - k, log_n, log_m)?; + + let acc_alpha_first = acc_instance.alpha[0].clone(); + let acc_beta_0_first = acc_instance.beta.0[0].clone(); + let acc_beta_1_first = acc_instance.beta.1[0].clone(); + let acc_mu_first = acc_instance.mu[0]; + let l2_taus = parsed_acc.beta.0.clone(); + let l2_xs = parsed_acc.beta.1.clone(); + let l2_roots = parsed_acc.rt.clone(); + + let pesat_ior = Pesat:: { + code: &self.params.code, + hasher: &self.params.hasher, + _phantom: PhantomData, + }; + let twin_constraint_ior = TwinConstraint:: { + r1cs: self.params.p.constraints(), + _phantom: PhantomData, + }; + let bridge_ior = Bridge::::new(); + let ood_ior = Ood::::new(); + let sample_queries_ior = SampleQueries::::new(); + let batching_ior = Batching::::new(); + let proximity_ior = Proximity:: { + hasher: &self.params.hasher, + _phantom: PhantomData, + }; + + // ── IOR 1: PESAT ───────────────────────────────────────────── + let IorVerifyResult { + reduced: PesatReducedStatement { + mus: l1_mus, + taus: l1_taus, + }, + outputs: PesatVerifierOutputs { rt_0 }, + } = verify_ior!( + pesat_ior, + verifier_state, + statement: PesatStatement { l1, log_m }, + inputs: (), + )?; + + // ── IOR 2: TwinConstraint ──────────────────────────────────── + let IorVerifyResult { + reduced: TwinConstraintReducedStatement { + gamma, + zeta_0, + beta_tau, + deferred, + }, + outputs: _, + } = verify_ior!( + twin_constraint_ior, + verifier_state, + statement: TwinConstraintStatement { + acc_instance: parsed_acc, + l1_mus: l1_mus.clone(), + l1_taus: l1_taus.clone(), + log_l, + log_m, + log_n, + }, + inputs: (), + )?; + + // ── IOR 3: Bridge ──────────────────────────────────────────── + let IorVerifyResult { + reduced: BridgeReducedStatement { + eta: _, + nu_0, + td_new_root: _, + }, + outputs: _, + } = verify_ior!( + bridge_ior, + verifier_state, + statement: BridgeStatement { + zeta_0: zeta_0.clone(), + beta_tau, + log_m, + n_minus_k: N - k, + }, + inputs: BridgeVerifierInputs { + deferred: &deferred, + gamma: &gamma, + }, + )?; + + // ── IOR 4: OOD ─────────────────────────────────────────────── + let IorVerifyResult { + reduced: OodReducedStatement { samples_flat, answers }, + outputs: _, + } = verify_ior!( + ood_ior, + verifier_state, + statement: OodStatement { s: self.params.config.s, log_n }, + inputs: (), + )?; + + // ── IOR 5: SampleQueries ───────────────────────────────────── + let IorVerifyResult { + reduced: SampleQueriesReducedStatement { queries }, + outputs: _, + } = verify_ior!( + sample_queries_ior, + verifier_state, + statement: SampleQueriesStatement { log_n, t: self.params.config.t }, + inputs: (), + )?; + + (proof.shift_query_answers.len() == self.params.config.t) + .then_some(()) + .ok_or(VerifierError::NumShiftQueries)?; + (proof.auth_j.len() == l2) + .then_some(()) + .ok_or(VerifierError::NumL2Instances)?; + + let mut indexed: Vec<(usize, usize)> = queries + .leaf_positions + .iter() + .copied() + .enumerate() + .map(|(row, pos)| (pos, row)) + .collect(); + indexed.sort_by_key(|&(pos, _)| pos); + indexed.dedup_by_key(|&mut (pos, _)| pos); + let sorted_unique: Vec = indexed.iter().map(|&(p, _)| p).collect(); + let row_indices: Vec = indexed.iter().map(|&(_, r)| r).collect(); + + let fresh_values: Vec> = row_indices + .iter() + .map(|&r| proof.shift_query_answers[r][l2..].to_vec()) + .collect(); + let fresh_handle = MerkleIndexedOracle::new( + warp_scheme::(self.params.hasher.clone(), n), + &rt_0, + &proof.auth_0, + sorted_unique.clone(), + fresh_values, + ); + + let acc_handles: Vec> = (0..l2) + .map(|j| { + let acc_values: Vec> = row_indices + .iter() + .map(|&r| vec![proof.shift_query_answers[r][j]]) + .collect(); + MerkleIndexedOracle::new( + warp_scheme::(self.params.hasher.clone(), n), + &l2_roots[j], + &proof.auth_j[j], + sorted_unique.clone(), + acc_values, + ) + }) + .collect(); + + // ── IOR 7: Proximity ───────────────────────────────────────── + verify_ior!( + proximity_ior, + verifier_state, + statement: ProximityStatement { + queries: queries.clone(), + l2, + t: self.params.config.t, + n, + }, + inputs: ProximityVerifierInputs { + fresh: &fresh_handle, + acc: &acc_handles, + _f: PhantomData, + }, + )?; + + let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma); + let mut nus = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); + nus.push(nu_0); + nus.extend(answers); + for v_jk in proof.shift_query_answers.iter() { + let nu_st = v_jk + .iter() + .zip(&gamma_eq_evals) + .fold(F::zero(), |acc, (v, eq)| acc + *eq * *v); + nus.push(nu_st); + } + + // ── IOR 6: Batching ────────────────────────────────────────── + let IorVerifyResult { + reduced: BatchingReducedStatement { alpha }, + outputs: _, + } = verify_ior!( + batching_ior, + verifier_state, + statement: BatchingStatement::from_ior_outputs( + zeta_0.clone(), + &samples_flat, + &queries.evaluation_points, + self.params.config.s, + self.params.config.t, + log_n, + ), + inputs: BatchingVerifierInputs { + nus, + acc_mu: acc_mu_first, + }, + )?; + + (acc_alpha_first == alpha) + .then_some(()) + .ok_or(VerifierError::CodeEvaluationPoint)?; + + let betas = l2_taus + .into_iter() + .chain(l1_taus) + .zip(l2_xs.into_iter().chain(l1_xs)) + .map(|(tau_i, x)| concat_slices(&tau_i, &x)) + .collect::>>(); + let beta = scale_and_sum(&betas, &gamma_eq_evals); + let expected_beta = concat_slices(&acc_beta_0_first, &acc_beta_1_first); + (expected_beta == beta) + .then_some(()) + .ok_or(VerifierError::CircuitEvaluationPoint)?; + + Ok(()) + } +} diff --git a/tests/integration_warp.rs b/tests/integration_warp.rs index 6399105..e62e27c 100644 --- a/tests/integration_warp.rs +++ b/tests/integration_warp.rs @@ -32,8 +32,8 @@ use warp::relations::{ BundledPESAT, Relation, ToPolySystem, }; use warp::serialize::acc_witness_size; -use warp::traits::AccumulationScheme; -use warp::types::{AccumulatorInstance, AccumulatorWitness, WARPProverKey, WARPVerifierKey}; +use warp::accumulation::AccumulationScheme; +use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProverKey, WARPVerifierKey}; use warp::utils::poseidon; use warp::WARP; diff --git a/tests/profile_json.rs b/tests/profile_json.rs index 1b195c4..6ea46a9 100644 --- a/tests/profile_json.rs +++ b/tests/profile_json.rs @@ -27,8 +27,8 @@ use warp::relations::{ }, BundledPESAT, Relation, ToPolySystem, }; -use warp::traits::AccumulationScheme; -use warp::types::{AccumulatorInstance, AccumulatorWitness, WARPProverKey}; +use warp::accumulation::AccumulationScheme; +use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProverKey}; use warp::utils::poseidon; use warp::WARP; diff --git a/tests/verifier_negative.rs b/tests/verifier_negative.rs index c639c94..3757c5a 100644 --- a/tests/verifier_negative.rs +++ b/tests/verifier_negative.rs @@ -39,8 +39,8 @@ use warp::relations::{ }, BundledPESAT, Relation, ToPolySystem, }; -use warp::traits::AccumulationScheme; -use warp::types::{AccumulatorInstance, AccumulatorWitness, WARPProof, WARPProverKey, WARPVerifierKey}; +use warp::accumulation::AccumulationScheme; +use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProof, WARPProverKey, WARPVerifierKey}; use warp::utils::poseidon; use warp::WARP; From 98b154da0943c3c83e5850a82a3a61188c915735 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 12 May 2026 16:12:49 +0200 Subject: [PATCH 58/68] cleanup --- README.md | 34 ++++++++++++++++++++++------ src/lib.rs | 2 -- src/protocol/ior.rs | 24 ++++++-------------- src/protocol/iors/batching.rs | 13 +++-------- src/protocol/iors/bridge.rs | 11 ++------- src/protocol/iors/ood.rs | 13 +++-------- src/protocol/iors/pesat.rs | 13 +++-------- src/protocol/iors/proximity.rs | 13 +++-------- src/protocol/iors/sample_queries.rs | 13 +++-------- src/protocol/iors/twin_constraint.rs | 13 +++-------- 10 files changed, 54 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index f85edcf..8550a54 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ 🌀 WARP 🌀 Implementation repo for [WARP](https://eprint.iacr.org/2025/753) — an -accumulation scheme over R1CS with code-based commitments. +accumulation scheme with code-based commitments. ## Status -Research artifact. Reproduces the protocol from the paper. Not packaged -for crates.io (depends on git branches of `ark-vc`, `ark-codes`, and -`effsc`). +Ongoing research. ## Quick start @@ -51,8 +49,26 @@ for batch in batches { warp.decide(acc_w, acc_x)?; ``` -`examples/profile_iors.rs` runs the full prover and prints a per-IOR -wall-time breakdown when built with `--features profile`. +## Picking `(s, t)` for a target security level + +The `warp-params` binary picks and validates soundness parameters per +`docs/paper-mods/mod4_parameter_selection.tex`: + +```sh +# Pick (s, t) for λ bits of security at a given code rate and field size. +cargo run --release --bin warp-params -- select \ + --lambda 128 --rate 1/2 --field-bits 64 --regime conjectured + +# Check that a specific (s, t) actually hits λ bits. +cargo run --release --bin warp-params -- validate \ + --s 8 --t 128 --lambda 128 --rate 1/2 --field-bits 64 --regime conjectured + +# Dump the attested presets as TSV. +cargo run --release --bin warp-params -- table +``` + +`--regime` picks between `provable` and `conjectured` proximity bounds. +Exit codes: 0 ok, 1 derivation failed / target not met, 2 bad args. ## Layout @@ -63,15 +79,19 @@ wall-time breakdown when built with `--features profile`. - `src/protocol/transcript/` — transcript absorb / parse helpers - `src/accumulation.rs` — the `AccumulationScheme` trait - `src/relations/` — `R1CS`, `BundledPESAT`, `HashChainRelation` +- `src/params/` — soundness-driven `(s, t)` selection backing `warp-params` +- `src/bin/warp-params.rs` — CLI front-end for `src/params/` - `src/crypto/`, `src/utils/` — Merkle wrapper, field / poly helpers - `src/profile/` — opt-in tracing layer (gated behind the `profile` feature) - `tests/integration_warp.rs` — end-to-end on BLS12-381 and Goldilocks - `tests/verifier_negative.rs` — single-tamper rejection tests -## Running tests / benches +## Running tests / benches / profile ```sh cargo test --release cargo bench + +# Per-IOR wall-time breakdown of a prove run. cargo run --release --features profile --example profile_iors ``` diff --git a/src/lib.rs b/src/lib.rs index c3c7c07..fa50f7c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,3 @@ -#![allow(clippy::assign_op_pattern)] // generated by SmallFpConfig derive macro - pub mod accumulation; pub mod config; pub mod constraints; diff --git a/src/protocol/ior.rs b/src/protocol/ior.rs index dc09e98..8118f56 100644 --- a/src/protocol/ior.rs +++ b/src/protocol/ior.rs @@ -1,12 +1,16 @@ //! The Interactive Oracle Reduction abstraction. Paired spec: //! `docs/paper-mods/mod1_oracle.tex`. -#![allow(clippy::type_complexity)] + use spongefish::{ProverState, VerifierState}; use crate::error::{ProverError, VerifierError}; +/// Return type of [`IOR::prove_inner`] / [`IOR::prove`]: +/// `Result<(stmt-like, proof-string, reduced-witness), _>`. +pub type ProverTriple = Result<(A, P, W), ProverError>; + /// Interactive Oracle Reduction. `(stmt, wit, oracles_in) -> (stmt', /// oracles_out)`. Implementors write `prove_inner` / `verify_inner` / /// `reduce_statement`; the default `prove` / `verify` chain them so the @@ -48,14 +52,7 @@ pub trait IOR { statement: &Self::Statement<'a>, witness: &Self::Witness<'a>, inputs: &Self::ProverInputs<'a>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > + ) -> ProverTriple where Self: 'a; @@ -74,14 +71,7 @@ pub trait IOR { statement: &Self::Statement<'a>, witness: &Self::Witness<'a>, inputs: &Self::ProverInputs<'a>, - ) -> Result< - ( - Self::ReducedStatement, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > + ) -> ProverTriple where Self: 'a, { diff --git a/src/protocol/iors/batching.rs b/src/protocol/iors/batching.rs index bf024e1..13d27fa 100644 --- a/src/protocol/iors/batching.rs +++ b/src/protocol/iors/batching.rs @@ -36,9 +36,9 @@ use std::collections::HashMap; use std::marker::PhantomData; use crate::count_ops; -use crate::error::{ProverError, VerifierError}; +use crate::error::VerifierError; use crate::protocol::oracles::evaluation::Oracle; -use crate::protocol::ior::IOR; +use crate::protocol::ior::{ProverTriple, IOR}; use crate::protocol::transcript::EffscVerifierTranscript; use crate::utils::poly::{eq_poly, eq_poly_non_binary}; @@ -224,14 +224,7 @@ where statement: &Self::Statement<'b>, _witness: &Self::Witness<'b>, inputs: &Self::ProverInputs<'b>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > + ) -> ProverTriple where Self: 'b, 'a: 'b, diff --git a/src/protocol/iors/bridge.rs b/src/protocol/iors/bridge.rs index 452cd26..a5cb344 100644 --- a/src/protocol/iors/bridge.rs +++ b/src/protocol/iors/bridge.rs @@ -47,7 +47,7 @@ use crate::crypto::merkle::{warp_scheme, WarpCommitted}; use crate::error::{ProverError, VerifierError}; use crate::protocol::oracles::evaluation::Oracle; use crate::protocol::iors::twin_constraint::DeferredOracleCheck; -use crate::protocol::ior::IOR; +use crate::protocol::ior::{ProverTriple, IOR}; use crate::relations::BundledPESAT; pub struct BridgeStatement { @@ -194,14 +194,7 @@ where statement: &Self::Statement<'a>, witness: &Self::Witness<'a>, inputs: &Self::ProverInputs<'a>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > + ) -> ProverTriple where Self: 'a, { diff --git a/src/protocol/iors/ood.rs b/src/protocol/iors/ood.rs index 8d3785b..df5a0e2 100644 --- a/src/protocol/iors/ood.rs +++ b/src/protocol/iors/ood.rs @@ -24,9 +24,9 @@ use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState use std::marker::PhantomData; use crate::count_ops; -use crate::error::{ProverError, VerifierError}; +use crate::error::VerifierError; use crate::protocol::oracles::evaluation::Oracle; -use crate::protocol::ior::IOR; +use crate::protocol::ior::{ProverTriple, IOR}; pub struct OodStatement { pub s: usize, @@ -118,14 +118,7 @@ where statement: &Self::Statement<'b>, _witness: &Self::Witness<'b>, inputs: &Self::ProverInputs<'b>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > + ) -> ProverTriple where Self: 'b, { diff --git a/src/protocol/iors/pesat.rs b/src/protocol/iors/pesat.rs index deb516a..c80ac34 100644 --- a/src/protocol/iors/pesat.rs +++ b/src/protocol/iors/pesat.rs @@ -24,8 +24,8 @@ use std::marker::PhantomData; use crate::count_ops; use crate::crypto::merkle::{encode_codewords, warp_scheme, WarpCommitted}; -use crate::error::{ProverError, VerifierError}; -use crate::protocol::ior::IOR; +use crate::error::VerifierError; +use crate::protocol::ior::{ProverTriple, IOR}; pub struct PesatStatement { pub l1: usize, @@ -126,14 +126,7 @@ where statement: &Self::Statement<'b>, witness: &Self::Witness<'b>, _inputs: &Self::ProverInputs<'b>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > + ) -> ProverTriple where 'a: 'b, H: 'b, diff --git a/src/protocol/iors/proximity.rs b/src/protocol/iors/proximity.rs index 0cd19ff..a3c7146 100644 --- a/src/protocol/iors/proximity.rs +++ b/src/protocol/iors/proximity.rs @@ -25,9 +25,9 @@ use std::marker::PhantomData; use crate::count_ops; use crate::crypto::merkle::{warp_scheme, WarpCommitted, WarpProof}; -use crate::error::{ProverError, VerifierError}; +use crate::error::VerifierError; use crate::protocol::oracles::indexed_merkle::IndexedOracle; -use crate::protocol::ior::IOR; +use crate::protocol::ior::{ProverTriple, IOR}; use crate::protocol::oracles::query_indices::QueryIndices; pub struct ProximityStatement { @@ -144,14 +144,7 @@ where statement: &Self::Statement<'b>, _witness: &Self::Witness<'b>, inputs: &Self::ProverInputs<'b>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > + ) -> ProverTriple where Self: 'b, 'a: 'b, diff --git a/src/protocol/iors/sample_queries.rs b/src/protocol/iors/sample_queries.rs index c1498b3..731fc1d 100644 --- a/src/protocol/iors/sample_queries.rs +++ b/src/protocol/iors/sample_queries.rs @@ -24,8 +24,8 @@ use ark_ff::Field; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; -use crate::error::{ProverError, VerifierError}; -use crate::protocol::ior::IOR; +use crate::error::VerifierError; +use crate::protocol::ior::{ProverTriple, IOR}; use crate::protocol::oracles::query_indices::QueryIndices; pub struct SampleQueriesStatement { @@ -108,14 +108,7 @@ where statement: &Self::Statement<'b>, _witness: &Self::Witness<'b>, _inputs: &Self::ProverInputs<'b>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > + ) -> ProverTriple where Self: 'b, { diff --git a/src/protocol/iors/twin_constraint.rs b/src/protocol/iors/twin_constraint.rs index c0b5967..ce9b7c9 100644 --- a/src/protocol/iors/twin_constraint.rs +++ b/src/protocol/iors/twin_constraint.rs @@ -47,9 +47,9 @@ use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState use std::marker::PhantomData; use crate::count_ops; -use crate::error::{ProverError, VerifierError}; +use crate::error::VerifierError; use crate::protocol::oracles::evaluation::Oracle; -use crate::protocol::ior::IOR; +use crate::protocol::ior::{ProverTriple, IOR}; use crate::protocol::transcript::EffscVerifierTranscript; use crate::relations::r1cs::R1CSConstraints; use crate::warp::AccumulatorInstance; @@ -370,14 +370,7 @@ where statement: &Self::Statement<'b>, witness: &Self::Witness<'b>, inputs: &Self::ProverInputs<'b>, - ) -> Result< - ( - Self::ReductionInputs, - Self::ProofString, - Self::ReducedWitness, - ), - ProverError, - > + ) -> ProverTriple where Self: 'b, 'a: 'b, From 8494fed58fbf6844d75c2e98d407588e5447a5c0 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 12 May 2026 16:53:23 +0200 Subject: [PATCH 59/68] trim: -630 lines via bloat review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Items 1-9 of the aggressive review: - delete docs/paper-mods/slides/{iors.tex,iors.pdf} (~2200 lines of beamer source/PDF that doesn't belong in this repo) - delete src/accumulation.rs trait — only ever impl'd by WARP; the trait + 30-line *_impl delegation layer was pure indirection - delete examples/profile_iors.rs — broken example (wrong hasher type, wrong WARP::new arity); not in CI - drop unused AccumulatorInstance / AccumulatorWitness is_empty/len - drop the //! IOR signature ... bullet blocks; the type aliases below are the spec - stateless IORs (Ood, SampleQueries, Batching, Bridge) become unit- ish tuple structs with #[derive(Default)] — kills the new()/Default/{<'a, F>{phantom...}} boilerplate - prelude module deleted; flatten to top-level re-exports - trim tracing-subscriber features from 5 to 3 (drop json, ansi) - aggressive comment trim everywhere: drop narrative module headers, field doc-comments that restate field names, section dividers in prove/verify --- Cargo.toml | 6 +- README.md | 8 +- benches/warp_rs.rs | 1 - docs/paper-mods/slides/iors.pdf | Bin 325389 -> 0 bytes docs/paper-mods/slides/iors.tex | 311 ------------------------- examples/profile_iors.rs | 196 ---------------- src/accumulation.rs | 44 ---- src/bin/warp-params.rs | 14 +- src/lib.rs | 18 +- src/params/mod.rs | 14 +- src/params/presets.rs | 25 +- src/params/select.rs | 23 +- src/params/types.rs | 39 +--- src/params/validate.rs | 12 - src/profile/counters.rs | 28 +-- src/profile/layer.rs | 25 +- src/profile/mod.rs | 25 +- src/profile/rss.rs | 19 +- src/profile/timing.rs | 15 +- src/protocol/iors/batching.rs | 76 +----- src/protocol/iors/bridge.rs | 76 +----- src/protocol/iors/ood.rs | 44 +--- src/protocol/iors/pesat.rs | 21 -- src/protocol/iors/proximity.rs | 13 -- src/protocol/iors/sample_queries.rs | 34 +-- src/protocol/iors/twin_constraint.rs | 77 +----- src/protocol/oracles/evaluation.rs | 26 +-- src/protocol/oracles/indexed_merkle.rs | 41 +--- src/protocol/oracles/query_indices.rs | 8 +- src/warp/accumulator.rs | 17 -- src/warp/decide.rs | 2 +- src/warp/prove.rs | 17 +- src/warp/scheme.rs | 54 +---- src/warp/verify.rs | 17 +- tests/integration_warp.rs | 1 - tests/profile_json.rs | 1 - tests/verifier_negative.rs | 1 - 37 files changed, 84 insertions(+), 1265 deletions(-) delete mode 100644 docs/paper-mods/slides/iors.pdf delete mode 100644 docs/paper-mods/slides/iors.tex delete mode 100644 examples/profile_iors.rs delete mode 100644 src/accumulation.rs diff --git a/Cargo.toml b/Cargo.toml index 00f799d..843bb5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ spongefish = { version = "0.7.0", features = ["ark-ff"] } effsc = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git" } thiserror = "2.0.16" tracing = { version = "0.1", default-features = false, features = ["std", "attributes"] } -tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi", "env-filter", "json", "registry"], optional = true } +tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "env-filter", "registry"], optional = true } libc = { version = "0.2", optional = true } ark-codes = { git = "https://github.com/z-tech/ark-codes.git", branch = "z-tech/arkworks-0.6.0" } ark-vc = { git = "https://github.com/arkworks-rs/ark-vc.git", branch = "z-tech/hash-regions" } @@ -42,10 +42,6 @@ default = ["asm"] asm = ["ark-ff/asm"] -# Opt-in profiling: brings in tracing-subscriber + libc so we can emit -# structured per-phase records (wall_ns, cpu_ns, peak_rss_delta, op counters). -# Release builds with `profile` off pull in neither tracing-subscriber nor -# libc as direct deps of `warp`. profile = ["dep:tracing-subscriber", "dep:libc"] [[bench]] diff --git a/README.md b/README.md index 8550a54..616b813 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Ongoing research. ## Quick start ```rust -use warp::prelude::*; +use warp::{WARPConfig, AccumulatorInstance, AccumulatorWitness, WARPProverKey, WARP}; use warp::utils::poseidon; use warp::relations::{r1cs::{R1CS, hashchain::HashChainRelation}, BundledPESAT, ToPolySystem}; use ark_codes::{reed_solomon::{ReedSolomon, config::ReedSolomonConfig}, traits::LinearCode}; @@ -77,7 +77,6 @@ Exit codes: 0 ok, 1 derivation failed / target not met, 2 bad args. - `src/protocol/iors/` — concrete IORs (`pesat`, `twin_constraint`, `bridge`, `ood`, `sample_queries`, `batching`, `proximity`) - `src/protocol/oracles/` — oracle vocabulary used by IORs - `src/protocol/transcript/` — transcript absorb / parse helpers -- `src/accumulation.rs` — the `AccumulationScheme` trait - `src/relations/` — `R1CS`, `BundledPESAT`, `HashChainRelation` - `src/params/` — soundness-driven `(s, t)` selection backing `warp-params` - `src/bin/warp-params.rs` — CLI front-end for `src/params/` @@ -86,12 +85,9 @@ Exit codes: 0 ok, 1 derivation failed / target not met, 2 bad args. - `tests/integration_warp.rs` — end-to-end on BLS12-381 and Goldilocks - `tests/verifier_negative.rs` — single-tamper rejection tests -## Running tests / benches / profile +## Running tests / benches ```sh cargo test --release cargo bench - -# Per-IOR wall-time breakdown of a prove run. -cargo run --release --features profile --example profile_iors ``` diff --git a/benches/warp_rs.rs b/benches/warp_rs.rs index a342a93..d1aad00 100644 --- a/benches/warp_rs.rs +++ b/benches/warp_rs.rs @@ -8,7 +8,6 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use utils::domainsep::init_prover_state; use utils::hash_chain::{get_hashchain_instance_witness_pairs, get_hashchain_r1cs}; use warp::config::WARPConfig; -use warp::accumulation::AccumulationScheme; use warp::warp::WARPProverKey; use warp::WARP; diff --git a/docs/paper-mods/slides/iors.pdf b/docs/paper-mods/slides/iors.pdf deleted file mode 100644 index 254f8090463d15925449628f39c99dbc7229f7ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 325389 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

    F80y~S{nM^OL#v!ID0@mU#{*H<~Uj5%^i9F&y< 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 H=YOswP_3J81Fc5bDf8H*)j7kmL5w2yv4r~T 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 diff --git a/docs/paper-mods/slides/iors.tex b/docs/paper-mods/slides/iors.tex deleted file mode 100644 index 4508c77..0000000 --- a/docs/paper-mods/slides/iors.tex +++ /dev/null @@ -1,311 +0,0 @@ -\documentclass[10pt,aspectratio=169]{beamer} - -\usepackage{listings} -\usepackage{xcolor} -\usepackage{booktabs} -\usepackage{array} - -\usetheme{metropolis} -\usepackage[scale=0.85]{FiraSans} - -\definecolor{warpaccent}{HTML}{2C3E50} -\definecolor{warpcode}{HTML}{1B5E20} -\definecolor{warpgray}{HTML}{555555} - -\setbeamercolor{frametitle}{bg=warpaccent} -\setbeamercolor{title separator}{fg=warpaccent} -\setbeamertemplate{frame numbering}[fraction] - -\lstset{ - basicstyle=\ttfamily\footnotesize, - keywordstyle=\color{warpaccent}\bfseries, - commentstyle=\color{warpgray}\itshape, - stringstyle=\color{warpcode}, - showstringspaces=false, - morekeywords={Self, dyn, impl, async, await, fn, type, trait, where, Result, Vec, Box, Option, mut, pub, struct, let, return, use}, - keepspaces=true, - columns=fullflexible, - frame=none, - breaklines=true, -} - -\title{Warp Modularity} -\subtitle{From paper IORs to a code-level interface} -\author{Internal note} -\date{} - -\begin{document} - -\maketitle - -% -------------------------------------------------------------------- -\begin{frame}{What we want} -A single interface every Warp phase satisfies, that is visibly the same -shape as the paper's IOR signature. - -\vspace{0.5em} -\begin{itemize} - \item \texttt{lib.rs::prove} reads as a chain of typed transitions. - \item Each transition: $(\text{stmt}, \text{wit}, \text{or}_\text{in}) \mapsto (\text{stmt}', \text{or}_\text{out})$. - \item Adding a phase: a struct + a trait impl. No bespoke plumbing. -\end{itemize} - -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}{Why Warp wasn't easy modularly} -The paper IOR formalism doesn't share state across IORs. Real Warp does. - -\vspace{0.4em} -\textbf{Three concrete frictions:} -\begin{enumerate} - \item \textbf{Shared oracle $f$.} TwinConstraint, OOD, Batching, Proximity all consume the same codeword. A literal IOR-per-phase would rematerialise. - \item \textbf{Transcript ordering coupling.} Shift-query indices are squeezed once and consumed by both Batching and Proximity. No phase ``owns'' the squeeze. - \item \textbf{Statement / witness / oracle conflation.} The pre-refactor function had $\sim$10 mixed-purpose args; the paper's tripartite split was invisible. -\end{enumerate} -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}{What an IOR signature is} -The Warp paper structures the protocol as a sequence of Interactive Oracle Reductions, each one having the composition rule: -\[ A: (\mathrm{stmt}_A, \mathrm{wit}_A) \mapsto (\mathrm{stmt}_A', \mathsf{O}[f]) \] -\[ B: (\mathrm{stmt}_B, \mathsf{O}[f]) \mapsto \mathrm{stmt}_B' \] -\[ A;B: (\mathrm{stmt}_A, \mathrm{stmt}_B, \mathrm{wit}_A) \mapsto (\mathrm{stmt}_A', \mathrm{stmt}_B') \] - -\vspace{0.4em} -Five typed components per IOR: - -\begin{center}\small -\begin{tabular}{lcc} -\toprule -& \textbf{Prover} & \textbf{Verifier} \\ -\midrule -\texttt{Statement} & \checkmark & \checkmark \\ -\texttt{Witness} & \checkmark & --- \\ -\texttt{InputOracles} & by data & by handle \\ -\texttt{ReducedStatement} & \checkmark & \checkmark \\ -\texttt{OutputOracles} & by data & by handle \\ -\bottomrule -\end{tabular} -\end{center} -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}[fragile]{The IOR trait we built} -\small -Current shape (post-PR\#22 review) mirrors the paper and routes both sides through one reduction function: -\begin{lstlisting} -pub trait IOR { - type Statement; type Witness; - type ProverInputs; type VerifierInputs; - type ReductionInputs; - type ReducedStatement; - type ProofString; type ReducedWitness; - type VerifierOutputs; - - fn reduce_statement(&self, &Stmt, &ReductionInputs) -> Reduced; - fn prove_inner(...) -> (ReductionInputs, ProofString, ReducedWitness); - fn verify_inner(...) -> (ReductionInputs, VerifierOutputs); - // default `prove` / `verify` chain *_inner -> reduce_statement -} -\end{lstlisting} -Implementor writes the three; defaults wire them. \texttt{Statement} / \texttt{ReducedStatement} shared; oracle types role-split; reduction is single-source-of-truth. 33/33 tests green. -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}{Per-phase mapping} -\scriptsize -\begin{center} -\begin{tabular}{lp{1.2cm}p{1.0cm}p{1.0cm}p{1.4cm}p{0.9cm}p{1.0cm}} -\toprule -& \textbf{Stmt} & \textbf{Wit} & \textbf{Pr.\,In} & \textbf{Reduced} & \textbf{Proof} & \textbf{Red.\,Wit} \\ -\midrule -\texttt{Pesat} & $(l_1, \log m)$ & wits & --- & $(\mu, \tau)$ & --- & cw + tree \\ -\texttt{TwinC.} & acc + $\mu$/$\tau$ & acc-w + ins & cw + acc-cw & $\gamma, \zeta_0, \beta_\tau,$ defer & --- & $\mathsf{O}[f] + z$ \\ -\texttt{Ood} & $(s, \log n)$ & --- & $\mathsf{O}[f]$ & samples + ans & --- & --- \\ -\texttt{Batching} & $\zeta$\,+\,$s,t,\log n$ & --- & $\mathsf{O}[f]$ & $\alpha$ & --- & $\mu$ \\ -\texttt{Proximity} & queries\,+\,$l_2,t$ & --- & trees + cw & $()$ & paths + ans & --- \\ -\bottomrule -\end{tabular} -\end{center} - -\vspace{0.4em} -\textbf{Asymmetries are now structural:} -\begin{itemize}\scriptsize - \item \texttt{Pesat} \,---\, empty \texttt{ProverInputs} (it's the source). - \item \texttt{Proximity} \,---\, empty \texttt{Reduced} and \texttt{Red.\,Wit} (it's a check; pure \texttt{ProofString}). - \item \texttt{Ood} / \texttt{Batching} \,---\, empty \texttt{Witness}. - \item \texttt{ProofString} is non-empty only for \texttt{Proximity}; every other phase's prover output is reduced-witness-shaped. -\end{itemize} -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}{Single-protocol view: a candid review} -\small -\textbf{What's good.} -\begin{itemize}\small - \item Paper-aligned types \,---\, the \texttt{IOR} trait and paper composition rule line up directly. - \item Verifier symmetry \,---\, \texttt{*::verify} are first-class trait impls. - \item Honest oracle role split \,---\, prover sees data, verifier sees \emph{handles} (see oracle slide). - \item Empty-type asymmetries visible (\texttt{ProofString = ()} for reductions; \texttt{Reduced = ()} for checks). - \item Statement reduction is single-source-of-truth \,---\, no prover/verifier drift. -\end{itemize} - -\vspace{0.4em} -\textbf{What's not.} -\begin{itemize}\small - \item Nine associated types per phase \,---\, real cognitive load. - \item Lifetimes \& \texttt{PhantomData} proliferate; structs carry \texttt{<'a, F, \dots>}. - \item No type-level ordering enforcement; transcript order is implicit. - \item Orchestrator got longer, not shorter \,---\, $\sim$10 lines per phase, plus oracle-handle construction. -\end{itemize} -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}{Reframing: a family of protocols} -The single-protocol view above was honest \,---\, but the actual goal isn't ``a clean WARP.'' - -\vspace{0.4em} -We design protocols like \textbf{STIR}, \textbf{WHIR}, \textbf{WARP} all the time. They share: -\begin{itemize}\small - \item Encode-and-commit (codeword \,---\, Reed-Solomon, or otherwise). - \item Sumcheck reductions (different flavours: degree-1, multilinear, fused twin-constraint, $\dots$). - \item Out-of-domain (OOD) sampling for proximity boost. - \item Shift-query opening with auth paths. - \item Batching sumchecks that fold many claims into one. - \item Accumulator updates / final consistency. -\end{itemize} - -\vspace{0.4em} -\textbf{The bar for the IOR trait then shifts:} not ``does this clean up WARP?'' but ``is this the right backbone for a family?'' -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}{The toolkit, audited} -What practitioners writing STIR / WHIR / WARP \emph{already} have: -\begin{itemize}\small - \item \textbf{Transcripts.} \texttt{spongefish} 0.7 \,---\, de facto in this corner of the ecosystem. Practitioners use it directly. No abstraction needed. - \item \textbf{Sumchecks.} \texttt{effsc} \,---\, \texttt{SumcheckProver}, \texttt{RoundPolyEvaluator}, \texttt{InnerProductProver}, \texttt{CoefficientProverLSB}, \texttt{MultilinearProver}, hypercube + protogalaxy utilities. WARP uses 9 of 10 effsc primitives already. - \item \textbf{Linear codes.} \texttt{ark-codes::LinearCode} \,---\, encode, code\_len. - \item \textbf{Vector commitments.} \texttt{ark-vc} (abstract trait) + \texttt{ark-mt} (Merkle backend, multi-vector + single-shot openings, Blake3 / Poseidon hashers). - \item \textbf{Oracle struct.} \texttt{Vec} codeword + lazy MLE cache. Same storage shape across STIR / WHIR / WARP. -\end{itemize} - -\vspace{0.4em} -The primitive layer is mostly done. The discussion is really about \emph{integration} \,---\, how primitives compose, and what shape protocol authors copy. -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}{What practitioner experience actually needs} -Designing a STIR / WHIR / WARP-style protocol is hard. The friction is rarely ``I need a new sumcheck primitive.'' It's: -\begin{itemize}\small - \item \textbf{Tying primitives together correctly.} LinearCode + Merkle is two crates; integration is per-protocol boilerplate. - \item \textbf{Knowing the right phase decomposition.} What's a phase? What's its statement? Where's the witness? When does the verifier act? - \item \textbf{Threading transcript ordering without bugs.} Squeeze before write, derive challenges in the right segments. - \item \textbf{Maintaining paper-code alignment.} The paper's IOR composition is the contract; reviewers expect to see it in code. - \item \textbf{Debugging when soundness fails.} ``Where in this 600-line \texttt{verify} did the rejection happen?'' -\end{itemize} - -\vspace{0.4em} -The fellow: \emph{practitioners benefit from patterns + examples + shared primitives}, not from cross-protocol trait contracts. That's the lever. -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}[fragile]{What WARP migrated onto: \texttt{ark-vc} / \texttt{ark-mt}} -\small -Replaced \texttt{ark-crypto-primitives::merkle\_tree} with a multi-vector commitment scheme. Two structural changes: - -\vspace{0.3em} -\textbf{1. Per-codeword $\to$ one tree.} PESAT commits $l_1$ fresh codewords; leaves are column-tuples $(c_0[i], \dots, c_{l_1-1}[i])$, hashed as \texttt{Vec}. One root authenticates all codewords. - -\vspace{0.3em} -\textbf{2. Per-query $\to$ one proof.} Was $t$ separate \texttt{Path}, each carrying $\log n$ siblings. Now a single path-pruned \texttt{OpeningProof} for all $t$ positions (union of copaths minus union of paths, book §20). - -\vspace{0.3em} -At $n{=}2^{15}$, $t{=}100$: $1500 \to \approx 730$ sibling hashes. Savings grow with $t$. - -\vspace{0.3em} -\textbf{Type diff:} \texttt{MerkleTree} $\to$ \texttt{MultiVectorCommitted}; \texttt{Path} $\to$ \texttt{OpeningProof>}; \texttt{H: MerkleHasher>} replaces \texttt{MT: Config} across all phases. -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}{What we're for, positively} -\textbf{Keep \texttt{IOR} as a structural template, not a cross-protocol contract.} -\begin{itemize}\small - \item It's the shape WARP-style protocols copy. The paper section on accumulator-as-state makes it legible. - \item Other protocols follow as a \emph{pattern}, get shared mental model + idioms, without being forced into one trait. -\end{itemize} - -\textbf{Lean on the existing primitive layer.} -\begin{itemize}\small - \item \texttt{effsc}, \texttt{spongefish}, \texttt{ark-codes}, \texttt{ark-vc} + \texttt{ark-mt}. - \item Already strong. The discipline is to use them, not reinvent. -\end{itemize} - -\textbf{WARP runs on \texttt{ark-vc}.} The missing integration trait was filled by \texttt{ark-vc}/\texttt{ark-mt}; WARP migrated onto its multi-vector commitment scheme. STIR-lite plugs into the same surface. - -\textbf{Make WARP the reference implementation.} A new protocol designer reads \texttt{src/protocol/phases/}, copies the IOR-shaped pattern, plugs in their primitives. - -\textbf{Document the pattern.} Short ``how to design a Warp-style protocol'' guide \,---\, narrative, not a trait. The actual lever for practitioner ergonomics. -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}[fragile]{\texttt{reduce\_statement} \,---\, eliminating prover/verifier drift (1 of 2)} -\small -\textbf{Problem.}\; -Prover and verifier each compute \texttt{ReducedStatement} via parallel-but-different code; they produce the same value by sumcheck-fold semantics, nothing structural keeps them in sync. Concrete site: TwinConstraint's $\zeta_0$ / $\beta_\tau$ \,---\, prover pulls from sumcheck CC's internal \texttt{tablewise()}, verifier recomputes via \texttt{scale\_and\_sum}. Patch one, forget the other, ship divergence. - -\vspace{0.3em} -\textbf{Change.} -\begin{lstlisting} -fn reduce_statement(&self, &Stmt, &ReductionInputs) -> ReducedStatement; -fn prove_inner(...) -> (ReductionInputs, ProofString, ReducedWitness); -fn verify_inner(...) -> (ReductionInputs, VerifierOutputs); -// default `prove`/`verify` chain *_inner -> reduce_statement. -\end{lstlisting} -Both sides land at \texttt{reduce\_statement} with the same \texttt{ReductionInputs}; the math runs in one place. - -\vspace{0.3em} -\textbf{Outcome.} Drift is structurally impossible: prover pulls only $f$, $z$ from CC; verifier stops parallel \texttt{scale\_and\_sum}. Cost: 1 associated type + 2 methods. 33/33 tests green. -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}[fragile]{\texttt{IndexedOracle} \,---\, BCS-agnostic verifier-side oracles (2 of 2)} -\small -\textbf{Problem.}\; -\texttt{ProximityVerifierInputs} carried \texttt{\{ rt\_0, l2\_roots, auth\_0, auth\_j, shift\_query\_answers \}} \,---\, a fully materialised BCS instantiation glued onto the IOR-level type. Slide 4 promises ``by handle''; the code delivered ``by precomputed table plus auth artifacts.'' Phase verifier knew about Merkle paths. - -\vspace{0.3em} -\textbf{Change.} -\begin{lstlisting} -trait IndexedOracle { fn query(&self, i: usize) -> Option; ... } -struct MerkleIndexedOracle<'a, F, H> { /* root, proof, idx+vals, OnceCell */ } -impl IndexedOracle> for MerkleIndexedOracle<...> { ... } -\end{lstlisting} -\texttt{ProximityVerifierInputs} is now generic over \texttt{O: IndexedOracle>} and holds \texttt{\&O} / \texttt{\&[O]} (static dispatch, no trait objects). BCS handle construction moves to the orchestrator. - -\vspace{0.3em} -\textbf{Outcome.} Phase code is BCS-agnostic \,---\, two \texttt{validate()} calls in \texttt{verify\_inner}. iBCS / ARG composition slots in via a different impl. Lazy validation \texttt{OnceCell}-memoised, so perf matches the prior eager check. - -\vspace{0.3em} -\textbf{Caveat.} Prover-side \texttt{Oracle} (OOD / Batching) is still concrete; sibling \texttt{EvalOracle} for point queries is a follow-up. -\end{frame} - -% -------------------------------------------------------------------- -\begin{frame}{Where this leaves us \,---\, the next move} -\small -\textbf{Already landed.}\; -\texttt{ark-vc}/\texttt{ark-mt} integration ($\sim$16\% smaller proof, prover matches with \texttt{parallel}). Four PR-\#22 follow-ups: \texttt{\&}-borrow, \texttt{ProofString}/\texttt{ReducedWitness} split, \texttt{IndexedOracle} handle, \texttt{reduce\_statement} split. Tests + benches green. - -\vspace{0.5em} -\textbf{Next.} -\begin{itemize}\small - \item Paper section \,---\, accumulator as typed state: IOR trait as paper-aligned \emph{template} for WARP-family protocols. - \item Paper section \,---\, structured sumcheck primitive: TwinConstraint's fused $\alpha/\beta/\tau$-fold extracted into \texttt{effsc} as a citable \texttt{RoundPolyEvaluator} impl. - \item Sibling \texttt{EvalOracle} \,---\, point-query analogue of \texttt{IndexedOracle} so prover-side OOD / Batching is BCS-agnostic too. - \item Second protocol \,---\, STIR-lite or WHIR-lite against the same primitives. Friction it surfaces is the next iteration's guide. - \item Practitioner narrative \,---\, ``how to design a Warp-style protocol,'' \texttt{docs/}. -\end{itemize} -\end{frame} - -\end{document} diff --git a/examples/profile_iors.rs b/examples/profile_iors.rs deleted file mode 100644 index df99277..0000000 --- a/examples/profile_iors.rs +++ /dev/null @@ -1,196 +0,0 @@ -//! Per-IOR wall-time profile of a Goldilocks WARP prove run. -//! -//! Run: `cargo run --release --features profile --example profile_iors`. -//! Best run with `RAYON_NUM_THREADS` pinned and other workloads off. - -#[cfg(not(feature = "profile"))] -fn main() { - eprintln!( - "profile_iors: build with --features profile (and preferably --release).\n\ - example: cargo run --release --features profile --example profile_iors" - ); -} - -#[cfg(feature = "profile")] -fn main() { - inner::run(); -} - -#[cfg(feature = "profile")] -mod inner { - use std::io::{self, Write}; - use std::sync::{Arc, Mutex}; - - use ark_codes::reed_solomon::config::ReedSolomonConfig; - use ark_codes::reed_solomon::ReedSolomon; - use ark_codes::traits::LinearCode; - use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; - use ark_crypto_primitives::merkle_tree::configs::Blake3MerkleConfig; - use ark_std::rand::thread_rng; - use ark_std::UniformRand; - - use warp::config::WARPConfig; - use warp::relations::{ - r1cs::{ - hashchain::{ - compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness, - }, - R1CS, - }, - BundledPESAT, Relation, ToPolySystem, - }; - use warp::accumulation::AccumulationScheme; - use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProverKey}; - use warp::utils::fields::Goldilocks; - use warp::utils::poseidon; - use warp::WARP; - - #[derive(Clone)] - struct SharedBuf(Arc>>); - - impl Write for SharedBuf { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.lock().unwrap().extend_from_slice(buf); - Ok(buf.len()) - } - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - } - - pub fn run() { - let iters = std::env::var("WARP_PROFILE_ITERS") - .ok() - .and_then(|s| s.parse::().ok()) - .unwrap_or(5); - let hash_chain_size = std::env::var("WARP_PROFILE_HCSIZE") - .ok() - .and_then(|s| s.parse::().ok()) - .unwrap_or(10); - let warmup = 2usize; - - let sink = SharedBuf(Arc::new(Mutex::new(Vec::new()))); - let installed = warp::profile::init_json(sink.clone()); - assert!(installed, "failed to install JSON profile subscriber"); - - let l1 = 4; - let s = 8; - let t = 7; - let mut rng = thread_rng(); - let poseidon_config = poseidon::initialize_poseidon_config::(); - let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( - poseidon_config.clone(), - hash_chain_size, - )) - .unwrap(); - let code_config = - ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); - let code = ReedSolomon::new(code_config); - let n = code.code_len(); - - let (instances, witnesses): (Vec<_>, Vec<_>) = (0..l1) - .map(|_| { - let preimage = vec![Goldilocks::rand(&mut rng)]; - let instance = HashChainInstance { - digest: compute_hash_chain::>( - &poseidon_config, - &preimage, - hash_chain_size, - ), - }; - let witness = HashChainWitness::>::new(preimage); - let relation = HashChainRelation::, CRHGadget<_>>::new( - instance, - witness, - (poseidon_config.clone(), hash_chain_size), - ); - (relation.x, relation.w) - }) - .unzip(); - - let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); - let prover = WARP::, _, Blake3MerkleConfig>::new( - warp_config, - code.clone(), - r1cs.clone(), - (), - (), - ); - - println!( - "=== warp profile (Goldilocks hashchain) — n={n}, l1={l1}, s={s}, t={t}, hashchain_size={hash_chain_size}, iters={iters} (+ {warmup} warmup)" - ); - - for i in 0..(warmup + iters) { - let domainsep = spongefish::domain_separator!("profile_phases"); - let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); - if i == warmup { - sink.0.lock().unwrap().clear(); - } - prover - .prove( - WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, - &mut prover_state, - witnesses.clone(), - instances.clone(), - AccumulatorInstance::empty(), - AccumulatorWitness::empty(), - ) - .unwrap(); - } - - let bytes = sink.0.lock().unwrap().clone(); - let text = String::from_utf8(bytes).expect("JSON output is UTF-8"); - - let rows: &[(&str, &str)] = &[ - ("pesat.encode", "rs_encode (FFT)"), - ("pesat.merkle_commit", "pesat_merkle_tree"), - ("twin_constraint.sumcheck", "twin_constraint_sumcheck"), - ("warp.commit_new_oracle", "merkle_commit"), - ("batching.eq_evals", "eq_poly_evals + ood_evals_vec"), - ("batching.sumcheck", "batching_sumcheck (inner product)"), - ("warp.prove", "end-to-end prover"), - ]; - - let mut series: std::collections::BTreeMap<&str, Vec> = - std::collections::BTreeMap::new(); - for line in text.lines() { - for (phase, _) in rows.iter() { - let needle = format!("\"phase\":\"{phase}\""); - if line.contains(&needle) { - if let Some(w) = extract_u64(line, "\"wall_ns\":") { - series.entry(*phase).or_default().push(w); - } - break; - } - } - } - - println!(); - println!("| Phase | n | mean (ms) | min (ms) | samples |"); - println!("|-------|---|-----------|----------|---------|"); - for (phase, label) in rows { - let vs = series.remove(*phase).unwrap_or_default(); - if vs.is_empty() { - println!("| {label} | {n} | n/a | n/a | 0 |"); - continue; - } - let sum_ns: u128 = vs.iter().map(|&v| v as u128).sum(); - let mean_ms = (sum_ns as f64 / vs.len() as f64) / 1e6; - let min_ms = *vs.iter().min().unwrap() as f64 / 1e6; - println!( - "| {label} | {n} | {mean_ms:.3} | {min_ms:.3} | {} |", - vs.len() - ); - } - } - - fn extract_u64(line: &str, key: &str) -> Option { - let i = line.find(key)? + key.len(); - let rest = &line[i..]; - let end = rest - .find(|c: char| !c.is_ascii_digit()) - .unwrap_or(rest.len()); - rest[..end].parse().ok() - } -} diff --git a/src/accumulation.rs b/src/accumulation.rs deleted file mode 100644 index 3e3bc54..0000000 --- a/src/accumulation.rs +++ /dev/null @@ -1,44 +0,0 @@ -use ark_ff::Field; -use ark_mt::MerkleHasher; -use spongefish::{ProverState, VerificationResult, VerifierState}; - -use crate::error::{VerifierError, WARPError}; -use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; -use crate::warp::proof::{ProveResult, WARPProof}; - -pub trait AccumulationScheme>> { - type Index; - type ProverKey; - type VerifierKey; - type Instances; - type Witnesses; - - fn index( - prover_state: &mut ProverState, - index: Self::Index, - ) -> VerificationResult<(Self::ProverKey, Self::VerifierKey)>; - - fn prove( - &self, - pk: Self::ProverKey, - prover_state: &mut ProverState, - witnesses: Self::Witnesses, - instances: Self::Instances, - acc_instance: AccumulatorInstance, - acc_witness: AccumulatorWitness, - ) -> ProveResult; - - fn verify<'a>( - &self, - vk: Self::VerifierKey, - verifier_state: &mut VerifierState<'a>, - acc_instance: AccumulatorInstance, - proof: WARPProof, - ) -> Result<(), VerifierError>; - - fn decide( - &self, - acc_witness: AccumulatorWitness, - acc_instance: AccumulatorInstance, - ) -> Result<(), WARPError>; -} diff --git a/src/bin/warp-params.rs b/src/bin/warp-params.rs index 2015992..5aa0588 100644 --- a/src/bin/warp-params.rs +++ b/src/bin/warp-params.rs @@ -1,17 +1,5 @@ -//! `warp-params` — CLI for exploring parameter selection. -//! +//! `warp-params` — CLI for soundness-driven parameter selection. //! Paired spec: `docs/paper-mods/mod4_parameter_selection.tex`. -//! -//! Usage: -//! -//! ```text -//! warp-params select --lambda 128 --rate 0.5 --field-bits 254 --regime conjectured -//! warp-params validate --s 8 --t 128 --lambda 128 --rate 0.5 --field-bits 254 --regime conjectured -//! warp-params table # dump PRESETS as a TSV -//! ``` -//! -//! This is intentionally dependency-free (no clap). Keeps the crate slim -//! and the exit semantics simple. use std::process::ExitCode; diff --git a/src/lib.rs b/src/lib.rs index fa50f7c..36ced41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -pub mod accumulation; pub mod config; pub mod constraints; pub mod crypto; @@ -11,15 +10,8 @@ pub mod serialize; pub mod utils; pub mod warp; -/// Re-exports the handful of items every WARP consumer needs. Use as -/// `use warp::prelude::*;`. -pub mod prelude { - pub use crate::accumulation::AccumulationScheme; - pub use crate::config::WARPConfig; - pub use crate::error::{DeciderError, ProverError, VerifierError, WARPError}; - pub use crate::warp::{ - AccumulatorInstance, AccumulatorWitness, WARPProof, WARPProverKey, WARPVerifierKey, WARP, - }; -} - -pub use crate::warp::WARP; +pub use crate::config::WARPConfig; +pub use crate::error::{DeciderError, ProverError, VerifierError, WARPError}; +pub use crate::warp::{ + AccumulatorInstance, AccumulatorWitness, WARPProof, WARPProverKey, WARPVerifierKey, WARP, +}; diff --git a/src/params/mod.rs b/src/params/mod.rs index 401d433..75e71f7 100644 --- a/src/params/mod.rs +++ b/src/params/mod.rs @@ -1,17 +1,5 @@ -//! Soundness-driven parameter selection for WARP. -//! +//! Soundness-driven `(s, t)` selection for WARP. //! Paired spec: `docs/paper-mods/mod4_parameter_selection.tex`. -//! -//! Given a security target, a field, a Reed–Solomon code rate, and a -//! choice of list-decoding regime, [`select`] returns the smallest -//! `(s, t)` tuple that achieves the target. [`validate`] is the inverse -//! and reports the soundness of an already-chosen tuple. -//! -//! The workload parameters `l` and `l1` are **not** chosen here — they -//! are caller-driven by the batch size the application actually needs. -//! -//! Derivation limits are called out in the companion `.tex`; the -//! formulas capture the dominant proximity-query term only. pub mod presets; pub mod select; diff --git a/src/params/presets.rs b/src/params/presets.rs index a9f7366..551ff86 100644 --- a/src/params/presets.rs +++ b/src/params/presets.rs @@ -1,14 +1,8 @@ -//! Pre-computed parameter tuples for common `(λ, code_rate)` points. -//! -//! Each entry is derived by [`super::select`] at the named regime and -//! stored here as a `const` lookup so tests and CLIs don't need to re-run -//! the derivation on every invocation. If you change the bounds in -//! `mod4_parameter_selection.tex`, regenerate this table via -//! `cargo run --bin warp-params -- table`. +//! Pre-computed `(λ, rate, regime) → (s, t)` rows. Regenerate via +//! `cargo run --bin warp-params -- table` after changing the bounds. use super::types::{Params, Regime, SecurityLevel}; -/// One row of [`PRESETS`]. pub struct Preset { pub lambda: SecurityLevel, pub code_rate_num: u32, @@ -23,14 +17,7 @@ impl Preset { } } -/// Common `(λ, rate, regime) → (s, t)` selections. See module docstring -/// on regeneration. -/// -/// All entries use the minimum `s = 8` (see -/// `mod4_parameter_selection.tex` §2); `t` is the smallest integer that -/// meets the target under the named regime. pub const PRESETS: &[Preset] = &[ - // λ=80 @ rate 1/2 Preset { lambda: SecurityLevel::STANDARD_80, code_rate_num: 1, @@ -45,7 +32,6 @@ pub const PRESETS: &[Preset] = &[ regime: Regime::Conjectured, params: Params { s: 8, t: 80 }, }, - // λ=80 @ rate 1/8 (three bits per query under provable) Preset { lambda: SecurityLevel::STANDARD_80, code_rate_num: 1, @@ -60,7 +46,6 @@ pub const PRESETS: &[Preset] = &[ regime: Regime::Conjectured, params: Params { s: 8, t: 27 }, }, - // λ=128 @ rate 1/2 Preset { lambda: SecurityLevel::STANDARD_128, code_rate_num: 1, @@ -75,7 +60,6 @@ pub const PRESETS: &[Preset] = &[ regime: Regime::Conjectured, params: Params { s: 8, t: 128 }, }, - // λ=128 @ rate 1/8 Preset { lambda: SecurityLevel::STANDARD_128, code_rate_num: 1, @@ -92,10 +76,7 @@ pub const PRESETS: &[Preset] = &[ }, ]; -/// Look up a preset by `(λ, num/den, regime)`. Exact-rational match — -/// callers that parsed the rate as a fraction preserve the exact form -/// and get a hit. Returns `None` if no exact row matches; use -/// [`super::select`] for arbitrary inputs. +/// Exact-rational match. `None` if the row isn't tabulated. pub fn lookup( lambda: SecurityLevel, num: u32, diff --git a/src/params/select.rs b/src/params/select.rs index 56f11a1..81827bd 100644 --- a/src/params/select.rs +++ b/src/params/select.rs @@ -1,31 +1,10 @@ -//! Soundness-driven parameter selection. -//! -//! Paired spec: `docs/paper-mods/mod4_parameter_selection.tex`. -//! -//! Computes the smallest `(s, t)` tuple that achieves the requested -//! security level under the chosen list-decoding regime. See the `.tex` -//! for the derivation and which bounds were used from the STIR / WHIR -//! literature. - use super::types::{ParamError, Params, Regime, SecurityLevel}; -/// Minimum OOD samples. Covers the constant term used in the current -/// derivation; tightening this against the batching-sumcheck soundness -/// is deferred — see `mod4_parameter_selection.tex` §2. const S_MIN: usize = 8; -/// Additive slack on the field-size admissibility check: we require -/// `log₂|F| ≥ λ + FIELD_EPSILON` so the polylog noise terms are -/// negligible at the target level. +/// We require `log₂|F| ≥ λ + FIELD_EPSILON` so polylog noise is negligible. pub const FIELD_EPSILON: u32 = 40; -/// Select the minimum `(s, t)` achieving `lambda` bits of soundness for -/// a Reed–Solomon code of rate `code_rate` over a field of `field_bits`, -/// under the chosen regime. -/// -/// Returns `Err(ParamError::InvalidRate)` if `code_rate ∉ (0, 1)`, and -/// `Err(ParamError::FieldTooSmall)` if the field cannot cover the polylog -/// noise at the requested target (see §3 of the paper-mods spec). pub fn select( lambda: SecurityLevel, field_bits: u32, diff --git a/src/params/types.rs b/src/params/types.rs index c2e73c8..f2b335e 100644 --- a/src/params/types.rs +++ b/src/params/types.rs @@ -1,9 +1,4 @@ -//! Parameter-selection types. -//! -//! Paired spec: `docs/paper-mods/mod4_parameter_selection.tex`. - -/// Target soundness, in bits. `SecurityLevel(128)` means the soundness -/// error should be at most 2⁻¹²⁸. +/// Target soundness in bits: `SecurityLevel(128)` means error ≤ 2⁻¹²⁸. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct SecurityLevel(pub u32); @@ -19,63 +14,35 @@ impl SecurityLevel { } } -/// Which list-decoding regime to assume when bounding proximity-query -/// soundness. See `docs/paper-mods/mod4_parameter_selection.tex` §2. +/// Which list-decoding regime to assume. `Provable`: Johnson bound (radius +/// `1 − √ρ`). `Conjectured`: list-decoding to `1 − ρ`; halves `t`. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Regime { - /// Johnson bound — radius `1 − √ρ`. Soundness error per query is - /// `√ρ`; proven for Reed–Solomon in the STIR / WHIR lineage. Provable, - /// Conjectured list-decodability up to radius `1 − ρ`. Soundness - /// error per query is `ρ`; halves the required query count vs. - /// provable at the same target. Conjectured, } -/// Selected security-driven WARP parameters. -/// -/// Workload parameters (`l`, `l1`) are *not* chosen here — they're caller- -/// supplied based on the batch size the application needs. This struct -/// carries only the choices whose minimum is dictated by soundness. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Params { - /// OOD samples. pub s: usize, - /// Proximity / shift queries. pub t: usize, } -/// Result of [`crate::params::validate`]. Reports the log₂ soundness error -/// achievable under the given `(params, rate, regime)`, together with the -/// field-size admissibility check. #[derive(Clone, Copy, Debug)] pub struct SoundnessBound { - /// `-log₂` of the proximity-query soundness error contribution. pub proximity_bits: f64, - /// Whether the field is large enough that the polylog-noise - /// contributions are negligible at the target level. pub field_admissible: bool, - /// Whether the selected `s` meets the minimum OOD sample count - /// used in the current formulas. Currently always true with the - /// hard-coded `S_MIN`; kept as a field so a future refinement can - /// surface a failure. pub ood_admissible: bool, } impl SoundnessBound { - /// `true` iff every component check passes at the caller's target. pub fn meets(&self, target: SecurityLevel) -> bool { self.field_admissible && self.ood_admissible && self.proximity_bits >= target.bits() as f64 } } -/// Reasons [`crate::params::select`] or [`crate::params::validate`] can -/// reject a configuration. Never panics. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ParamError { - /// Code rate was out of range `(0, 1)`. InvalidRate, - /// Field is too small to support the target soundness even with - /// infinite queries. FieldTooSmall { field_bits: u32, lambda: u32 }, } diff --git a/src/params/validate.rs b/src/params/validate.rs index c0ce171..ab0455d 100644 --- a/src/params/validate.rs +++ b/src/params/validate.rs @@ -1,20 +1,8 @@ -//! Soundness validation — the inverse of [`super::select`]. -//! -//! Given a `Params`, report how many bits of security it gives under the -//! chosen regime, and whether each component check passes. - use super::select::FIELD_EPSILON; use super::types::{ParamError, Params, Regime, SecurityLevel, SoundnessBound}; const S_MIN: usize = 8; -/// Compute the soundness bound that `params` achieves on a rate-`code_rate` -/// Reed–Solomon code over a field of `field_bits`, under `regime`. -/// -/// Returns `Err(ParamError::InvalidRate)` if the rate is outside `(0, 1)`. -/// `FieldTooSmall` is not returned here — field admissibility is surfaced -/// on the returned [`SoundnessBound`] so callers can reason about partial -/// failures. pub fn validate( params: &Params, field_bits: u32, diff --git a/src/profile/counters.rs b/src/profile/counters.rs index 3aa6077..b7a3f78 100644 --- a/src/profile/counters.rs +++ b/src/profile/counters.rs @@ -1,20 +1,9 @@ -//! Thread-local op counters. -//! -//! Call-site counters for operations we can cheaply observe at the -//! boundaries of our crate (Merkle tree builds, path generations, MLE -//! materialisations, sumcheck rounds). Field-level ops (muls/adds) are -//! **not** counted here — they would require newtyping `F` or forking -//! arkworks. That's deferred; Plan O's goal is asymptotic validation, not -//! per-op accounting. -//! -//! All counters compile to no-ops without the `profile` feature: the -//! [`count_ops!`] macro expands to `()`. +//! Thread-local op counters for crate-boundary events (Merkle ops, MLE +//! materialisations, sumcheck rounds). No-op without the `profile` feature. #[cfg(feature = "profile")] use std::cell::Cell; -/// Counter slots. Adding a new one: extend the enum, extend [`Counters`], -/// extend the [`count_ops!`] match, extend [`snapshot`] and [`delta`]. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum Counter { EncodeCalls, @@ -111,7 +100,6 @@ impl Counters { } } -/// Bump a counter on the current thread. #[cfg(feature = "profile")] #[inline] pub fn bump(c: Counter, n: u64) { @@ -125,7 +113,6 @@ pub fn bump(c: Counter, n: u64) { #[inline] pub fn bump(_c: Counter, _n: u64) {} -/// Snapshot every counter on the current thread. #[cfg(feature = "profile")] pub fn snapshot() -> Snapshot { let mut values = [0u64; Counter::ALL.len()]; @@ -142,8 +129,6 @@ pub fn snapshot() -> Snapshot { Snapshot {} } -/// A point-in-time reading of all counters. Subtract two snapshots with -/// [`Snapshot::delta`] to get the change over an interval. #[cfg(feature = "profile")] #[derive(Clone, Copy, Debug)] pub struct Snapshot { @@ -217,15 +202,6 @@ impl Delta { } } -/// Increment a named counter by 1 (or by a caller-supplied amount). -/// -/// Under `profile` feature: compiles to a thread-local Cell bump. -/// Without `profile`: compiles to `()`. -/// -/// ```ignore -/// count_ops!(MerkleTreeBuilds); // +1 -/// count_ops!(OraclePointQueries, 3); // +3 -/// ``` #[macro_export] macro_rules! count_ops { ($counter:ident) => { diff --git a/src/profile/layer.rs b/src/profile/layer.rs index 98000ea..1c27fb3 100644 --- a/src/profile/layer.rs +++ b/src/profile/layer.rs @@ -1,26 +1,5 @@ -//! Tracing Layer that emits one JSON record per closed span. -//! -//! Span fields are captured via a [`FieldVisitor`] on `on_new_span`; -//! counter / timing snapshots are stashed in span extensions on -//! `on_enter` and differenced on `on_close`. -//! -//! Schema (version `warp.profile.v1`): -//! -//! ```json -//! { -//! "schema": "warp.profile.v1", -//! "phase": "twin_constraint", -//! "wall_ns": 123456, -//! "cpu_ns": 98765, -//! "rss_delta_bytes": 1024, -//! "counters": { "twin_constraint_rounds": 10, ... }, -//! "dimensions": { "log_l": 3, "log_m": 2, "log_n": 5 } -//! } -//! ``` -//! -//! One record per line (newline-delimited JSON), written to the configured -//! `io::Write` sink. The enclosing module gates this file behind the -//! `profile` feature; no per-file `cfg` is needed here. +//! Tracing Layer that emits one `warp.profile.v1` JSON record per closed +//! span: `{phase, wall_ns, cpu_ns, rss_delta_bytes, counters, dimensions}`. use std::collections::BTreeMap; use std::io::Write; diff --git a/src/profile/mod.rs b/src/profile/mod.rs index 351a213..f4379e9 100644 --- a/src/profile/mod.rs +++ b/src/profile/mod.rs @@ -1,19 +1,6 @@ -//! Profile instrumentation. -//! -//! The library always emits `tracing` spans at IOR boundaries; this -//! module installs a subscriber that renders those spans. Rendering is -//! off by default — the `profile` cargo feature is required to bring in -//! `tracing-subscriber` and `libc` (for `clock_gettime` / `getrusage`). -//! -//! Two sinks are provided: -//! * [`init()`] / [`init_fmt()`] — human-readable text on stderr. -//! * [`init_json(writer)`] — newline-delimited JSON records to any -//! `io::Write` sink (stderr, a file, a pipe). Schema: -//! `warp.profile.v1`. See [`layer`] for the field list. -//! -//! Each installs a global subscriber the first time it is called; later -//! calls are no-ops. Without the feature, every init function returns -//! `false` and records nothing. +//! Opt-in profile instrumentation. The `profile` feature pulls in +//! `tracing-subscriber` and `libc` and lets `init_fmt`/`init_json` render +//! the always-emitted spans. Without the feature, init functions no-op. pub mod counters; pub mod rss; @@ -22,8 +9,6 @@ pub mod timing; #[cfg(feature = "profile")] pub mod layer; -/// Install a human-readable stderr subscriber (mimics the old -/// `[PROFILE]` lines). No-op without the `profile` feature. #[cfg(feature = "profile")] pub fn init() -> bool { init_fmt() @@ -34,8 +19,6 @@ pub fn init() -> bool { false } -/// Human-readable fmt subscriber on stderr. Reads `RUST_LOG` to pick a -/// filter; defaults to `warp=info`. #[cfg(feature = "profile")] pub fn init_fmt() -> bool { use tracing_subscriber::{fmt, prelude::*, EnvFilter}; @@ -58,8 +41,6 @@ pub fn init_fmt() -> bool { false } -/// Install the JSON subscriber. One `warp.profile.v1` record per closed -/// span is written to `writer`. Reads `RUST_LOG` like [`init_fmt`]. #[cfg(feature = "profile")] pub fn init_json(writer: W) -> bool where diff --git a/src/profile/rss.rs b/src/profile/rss.rs index 0d253fa..a1f95b5 100644 --- a/src/profile/rss.rs +++ b/src/profile/rss.rs @@ -1,20 +1,11 @@ -//! Peak resident-set-size accounting via `getrusage(RUSAGE_SELF)`. -//! -//! Linux reports `ru_maxrss` in kilobytes; macOS reports it in bytes. We -//! normalise to bytes. The kernel tracks the peak RSS for the process (not -//! the current thread), so this value is monotonic — useful only via -//! deltas across intervals. -//! -//! Returns `None` if the syscall fails. -//! -//! Without the `profile` feature this module compiles to stubs that always -//! return `None`. +//! Process peak RSS via `getrusage(RUSAGE_SELF)`. `ru_maxrss` is bytes on +//! macOS, kilobytes on Linux; we return bytes. Monotonic — use deltas. #[cfg(feature = "profile")] pub fn peak_rss_bytes() -> Option { #[cfg(any(target_os = "linux", target_os = "macos"))] { - // SAFETY: rusage is a POD; the kernel writes the full struct on success. + // SAFETY: rusage is POD; kernel writes on success. let mut ru: libc::rusage = unsafe { std::mem::zeroed() }; let rc = unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut ru) }; if rc != 0 { @@ -23,11 +14,11 @@ pub fn peak_rss_bytes() -> Option { let maxrss = ru.ru_maxrss as u64; #[cfg(target_os = "macos")] { - Some(maxrss) // bytes + Some(maxrss) } #[cfg(target_os = "linux")] { - Some(maxrss.saturating_mul(1024)) // kilobytes -> bytes + Some(maxrss.saturating_mul(1024)) } } #[cfg(not(any(target_os = "linux", target_os = "macos")))] diff --git a/src/profile/timing.rs b/src/profile/timing.rs index e0b5e64..d4020c8 100644 --- a/src/profile/timing.rs +++ b/src/profile/timing.rs @@ -1,14 +1,5 @@ -//! CPU-time accounting for the current thread. -//! -//! Wall time is available from `std::time::Instant`. CPU time is not — we -//! need `clock_gettime(CLOCK_THREAD_CPUTIME_ID)` (Linux / macOS Monterey+) -//! or `CLOCK_PROCESS_CPUTIME_ID` (older macOS) or `GetThreadTimes` -//! (Windows; not supported here). -//! -//! Returns `None` if the platform clock isn't available. -//! -//! Without the `profile` feature this module compiles to stubs that always -//! return `None`. +//! Thread CPU time via `clock_gettime(CLOCK_THREAD_CPUTIME_ID)`. `None` +//! off Linux/macOS or without the `profile` feature. #[cfg(feature = "profile")] pub fn thread_cpu_ns() -> Option { @@ -18,7 +9,7 @@ pub fn thread_cpu_ns() -> Option { tv_sec: 0, tv_nsec: 0, }; - // SAFETY: timespec is a POD; the kernel writes to it on success. + // SAFETY: timespec is POD; the kernel writes on success. let rc = unsafe { libc::clock_gettime(libc::CLOCK_THREAD_CPUTIME_ID, &mut ts) }; if rc == 0 { Some((ts.tv_sec as u64).saturating_mul(1_000_000_000) + (ts.tv_nsec as u64)) diff --git a/src/protocol/iors/batching.rs b/src/protocol/iors/batching.rs index 13d27fa..66e6a52 100644 --- a/src/protocol/iors/batching.rs +++ b/src/protocol/iors/batching.rs @@ -1,29 +1,6 @@ -//! Batching sumcheck IOR. -//! -//! Paired spec: `docs/paper-mods/mod1_oracle.tex` (oracle composition). -//! Reduces the batched claim -//! -//! ```text -//! Σ_i ξ(i) · \hat f(ζ_i) = σ₂ -//! ``` -//! -//! to a single evaluation claim `μ = \hat f(α)` via the inner-product -//! sumcheck, with the CBBZ23 / HyperPlonk sparse-evaluation optimization -//! (`accumulate_sparse_evaluations`) folded in. -//! -//! IOR signature -//! ------------- -//! - `Statement` — `(zetas_prefix, s, t, log_n)` — shared. -//! - `Witness` — `()` -//! - `ProverInputs` — `&Oracle` (the committed oracle, full data) -//! - `VerifierInputs` — `(nus, acc_mu)` — used to compute `σ₂` and the -//! final-claim oracle check. -//! - `ReductionInputs` — `alpha` (the LSB sumcheck challenges); both sides -//! compute and feed it through `reduce_statement`. -//! - `ReducedStatement` — `alpha` — the new code-eval point (LSB-indexed) -//! - `ProofString` — `()` -//! - `ReducedWitness` — `mu` — the prover's reported `\hat f(α)` -//! - `VerifierOutputs` — `()` +//! Batching sumcheck IOR. Reduces `Σ_i ξ(i)·f̂(ζ_i) = σ₂` to a single +//! claim `μ = f̂(α)` via inner-product sumcheck with the CBBZ23 / HyperPlonk +//! sparse-evaluation optimization. use ark_ff::{Field, PrimeField}; use ark_std::log2; @@ -42,9 +19,8 @@ use crate::protocol::ior::{ProverTriple, IOR}; use crate::protocol::transcript::EffscVerifierTranscript; use crate::utils::poly::{eq_poly, eq_poly_non_binary}; -/// [CBBZ23] / HyperPlonk sparse-evaluation optimization: for shift-query -/// zetas (indices `1+s..r`), each ζ is a 0/1 vector representing a single -/// hypercube point. +/// Sparse-eval optimization (CBBZ23 / HyperPlonk): shift-query zetas at +/// indices `1+s..r` are 0/1 vectors picking a single hypercube point. fn accumulate_sparse_evaluations( zetas: &[Vec], eq_evals: &[F], @@ -63,8 +39,6 @@ fn accumulate_sparse_evaluations( result } -/// Sum `dense_polys` column-wise and add the sparse contributions into the -/// resulting vector. fn batched_constraint_poly( dense_polys: &[Vec], sparse_polys: &HashMap, @@ -84,10 +58,8 @@ fn batched_constraint_poly( result } -// ─── IOR signature types ────────────────────────────────────────────────── - pub struct BatchingStatement { - /// `1 + s + t` evaluation points: `[ζ_0, ood_j…, query_k…]`. + /// `1 + s + t` evaluation points: `[ζ_0, ood_chunk_0…, query_k…]`. pub zetas_prefix: Vec>, pub s: usize, pub t: usize, @@ -96,11 +68,8 @@ pub struct BatchingStatement { } impl BatchingStatement { - /// Single source of truth for the `zetas_prefix` shape — both the - /// prover and verifier orchestrators construct their `BatchingStatement` - /// through this. Drift between sides becomes structurally impossible. - /// - /// Layout: `[ζ_0, ood_chunk_0, …, ood_chunk_{s-1}, query_0, …, query_{t-1}]`. + /// Single source of truth for `zetas_prefix` — both prover and verifier + /// build the statement through this so layout drift is impossible. pub fn from_ior_outputs( zeta_0: Vec, ood_samples_flat: &[F], @@ -132,47 +101,26 @@ pub struct BatchingProverInputs<'a, F: Field> { } pub struct BatchingVerifierInputs { - /// `1 + s + t` ν values; used to compute `σ₂ = Σ ξ_eq · ν`. pub nus: Vec, - /// Multiplier on the final-claim oracle check. pub acc_mu: F, } pub struct BatchingReductionInputs { - /// LSB-indexed sumcheck challenge vector. pub alpha: Vec, } pub struct BatchingReducedStatement { - /// New code-eval point (LSB-indexed). pub alpha: Vec, } pub struct BatchingReducedWitness { - /// `\hat f(α)` — prover's report. pub mu: F, } -/// Batching IOR configuration. -pub struct Batching<'a, F: Field> { - pub _phantom: PhantomData<&'a F>, -} - -impl<'a, F: Field> Batching<'a, F> { - pub fn new() -> Self { - Self { - _phantom: PhantomData, - } - } -} - -impl<'a, F: Field> Default for Batching<'a, F> { - fn default() -> Self { - Self::new() - } -} +#[derive(Default)] +pub struct Batching(PhantomData); -impl<'a, F> IOR for Batching<'a, F> +impl IOR for Batching where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { @@ -227,7 +175,6 @@ where ) -> ProverTriple where Self: 'b, - 'a: 'b, { let n = inputs.oracle.len(); let r = 1 + statement.s + statement.t; @@ -293,7 +240,6 @@ where ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> where Self: 'c, - 'a: 'c, { let r = 1 + statement.s + statement.t; let log_r = log2(r) as usize; diff --git a/src/protocol/iors/bridge.rs b/src/protocol/iors/bridge.rs index a5cb344..9fbbc6d 100644 --- a/src/protocol/iors/bridge.rs +++ b/src/protocol/iors/bridge.rs @@ -1,41 +1,5 @@ -//! TC → OOD bridge IOR. -//! -//! After TwinConstraint reduces to `(γ, ζ₀, β_τ)` on the verifier side -//! and `(γ, ζ₀, β_τ, deferred, f, z)` on the prover side, three new -//! values get published into the transcript: -//! -//! - `η = ⟨ eq(β_τ), p(z) ⟩` — bundled-PESAT evaluation -//! - `ν₀ = f̂(ζ₀)` — reduced-oracle evaluation at the new code point -//! - `td_new` — Merkle commit to `f`'s codeword (becomes the next -//! accumulator's root) -//! -//! The prover also splits `z` into `(new_x, new_w) = z[..N-k], z[N-k..]` -//! for the next accumulator's `(β.1, witness.w)` slots. -//! -//! The verifier reads `(td_digest, η, ν₀)` from the transcript and -//! discharges TwinConstraint's deferred oracle check: -//! -//! ```text -//! eq(τ, γ) · (ν₀ + ω · η) ≟ final_claim -//! ``` -//! -//! Modeled as an IOR for uniformity with the rest of the choreography, -//! even though it has no verifier challenges — Bridge is "0-round, -//! prover sends three values, verifier checks deferred." Same shape as -//! the IOR trait, just with degenerate fields where they don't apply. -//! -//! IOR signature -//! ------------- -//! - `Statement` — `(ζ₀, β_τ, log_m, n_minus_k)` -//! - `Witness` — `(z, f)` — prover's TC-side reduced state, borrowed -//! - `ProverInputs` — `(bundled_pesat, hasher, code_len)` — config refs -//! - `VerifierInputs` — `(deferred, γ)` — what's needed to discharge TC -//! - `ReductionInputs` — `(η, ν₀, td_new_root)` — transcript-published values -//! - `ReducedStatement` — same as `ReductionInputs` (the publish is the reduction) -//! - `ProofString` — `()` (everything goes through transcript writes / reads) -//! - `ReducedWitness` — `(td_new, new_x, new_w)` — fed into the new accumulator -//! - `VerifierOutputs` — `()` - +//! TC → OOD bridge IOR. Publishes `(td_new, η, ν₀)` and discharges +//! TwinConstraint's deferred oracle check `eq(τ,γ)·(ν₀ + ω·η) ≟ final_claim`. use ark_ff::Field; use ark_mt::MerkleHasher; use effsc::hypercube::compute_hypercube_eq_evals; @@ -101,43 +65,15 @@ where pub new_w: Vec, } -/// TC → OOD bridge IOR configuration. Stateless; the lifetime -/// parameter exists only to anchor `ProverInputs<'a>` / `Witness<'a>` -/// for the IOR trait impl. -pub struct Bridge<'a, F, P, H> -where - F: Field, - P: BundledPESAT, - H: MerkleHasher>, -{ - pub _phantom: PhantomData<(&'a F, &'a P, &'a H)>, -} +pub struct Bridge(PhantomData, PhantomData

    , PhantomData); -impl<'a, F, P, H> Bridge<'a, F, P, H> -where - F: Field, - P: BundledPESAT, - H: MerkleHasher>, -{ - pub fn new() -> Self { - Self { - _phantom: PhantomData, - } - } -} - -impl<'a, F, P, H> Default for Bridge<'a, F, P, H> -where - F: Field, - P: BundledPESAT, - H: MerkleHasher>, -{ +impl Default for Bridge { fn default() -> Self { - Self::new() + Self(PhantomData, PhantomData, PhantomData) } } -impl<'cfg, F, P, H> IOR for Bridge<'cfg, F, P, H> +impl IOR for Bridge where F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, P: BundledPESAT, diff --git a/src/protocol/iors/ood.rs b/src/protocol/iors/ood.rs index df5a0e2..0955c82 100644 --- a/src/protocol/iors/ood.rs +++ b/src/protocol/iors/ood.rs @@ -1,23 +1,4 @@ //! Out-of-domain sampling IOR. -//! -//! Paired spec: `docs/paper-mods/mod1_oracle.tex`. This IOR is a thin -//! composition of point queries on the committed oracle — see -//! [`Oracle::query_at_point`](crate::protocol::oracles::evaluation::Oracle::query_at_point). -//! The verifier derives the same random points from the transcript. -//! -//! IOR signature -//! ------------- -//! - `Statement` — `(s, log_n)` -//! - `Witness` — `()` -//! - `ProverInputs` — `&Oracle` (the committed oracle, full data) -//! - `VerifierInputs` — `()` (the oracle check is deferred to the batching -//! sumcheck's final claim) -//! - `ReductionInputs` — `(samples_flat, answers)` — both sides arrive here -//! from the same transcript reads and feed into `reduce_statement`. -//! - `ReducedStatement` — `(samples_flat, answers)` — query points + their answers -//! - `ProofString` — `()` -//! - `ReducedWitness` — `()` -//! - `VerifierOutputs` — `()` use ark_ff::{Field, PrimeField}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; @@ -43,33 +24,14 @@ pub struct OodReductionInputs { } pub struct OodReducedStatement { - /// Flat challenge vector of length `s · log_n`. pub samples_flat: Vec, - /// Answers `\hat f(ζ_j)` for each of the `s` chunked challenges. pub answers: Vec, } -/// OOD IOR configuration. Stateless; the lifetime parameter exists only -/// to anchor `ProverInputs<'a>` for the trait impl. -pub struct Ood<'a, F: Field> { - pub _phantom: PhantomData<&'a F>, -} - -impl<'a, F: Field> Ood<'a, F> { - pub fn new() -> Self { - Self { - _phantom: PhantomData, - } - } -} - -impl<'a, F: Field> Default for Ood<'a, F> { - fn default() -> Self { - Self::new() - } -} +#[derive(Default)] +pub struct Ood(PhantomData); -impl<'a, F> IOR for Ood<'a, F> +impl IOR for Ood where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { diff --git a/src/protocol/iors/pesat.rs b/src/protocol/iors/pesat.rs index c80ac34..e59b736 100644 --- a/src/protocol/iors/pesat.rs +++ b/src/protocol/iors/pesat.rs @@ -4,18 +4,6 @@ //! Merkle tree (one root over all l1 codewords), absorbs commitment + //! code evaluations, and derives the τ zero-check challenges. //! -//! IOR signature -//! ------------- -//! - `Statement` — `(l1, log_m)` -//! - `Witness` — `&[Vec]` (fresh witnesses to encode) -//! - `ProverInputs` — `()` -//! - `VerifierInputs` — `()` -//! - `ReductionInputs` — `(mus, taus)` — both sides derive from transcript -//! - `ReducedStatement` — `(mus, taus)` — code-eval claims + zero-check randomness -//! - `ProofString` — `()` -//! - `ReducedWitness` — full codewords + multi-vector commit -//! - `VerifierOutputs` — Merkle root only - use ark_codes::traits::LinearCode; use ark_ff::{Field, PrimeField}; use ark_mt::MerkleHasher; @@ -131,17 +119,14 @@ where 'a: 'b, H: 'b, { - // a. encode witnesses let codewords = { let _s = tracing::info_span!("pesat.encode").entered(); count_ops!(EncodeCalls, witness.witnesses.len() as u64); encode_codewords(self.code, witness.witnesses) }; - // b. evaluation claims let mus = codewords.iter().map(|f| f[0]).collect::>(); - // c. commit to interleaved codewords (multi-vector commitment) let td_0 = { let _s = tracing::info_span!("pesat.merkle_commit").entered(); count_ops!(MerkleTreeBuilds); @@ -149,7 +134,6 @@ where scheme.commit(&codewords) }; - // d. absorb commitment + claims; e/f. derive τ challenges. let taus = { let _s = tracing::info_span!("pesat.absorb_and_derive").entered(); prover_state.prover_message(td_0.root()); @@ -185,13 +169,8 @@ where 'a: 'c, H: 'c, { - // commitment digest let rt_0: H::Digest = verifier_state.prover_message()?; - - // mus (l1 evaluation claims) let mus: Vec = verifier_state.prover_messages_vec(statement.l1)?; - - // taus (l1 zero-check challenge vectors) let taus: Vec> = (0..statement.l1) .map(|_| { (0..statement.log_m) diff --git a/src/protocol/iors/proximity.rs b/src/protocol/iors/proximity.rs index a3c7146..e2cef18 100644 --- a/src/protocol/iors/proximity.rs +++ b/src/protocol/iors/proximity.rs @@ -5,19 +5,6 @@ //! producing one multi-opening proof per commitment and a flat table of //! the codeword values at those positions. //! -//! IOR signature -//! ------------- -//! - `Statement` — `(queries, l2, t, n)`. `n` is the codeword length; -//! the verifier needs it to construct the `WarpScheme` used for `check`. -//! - `Witness` — `()` -//! - `ProverInputs` — fresh `WarpCommitted` + l2 accumulated `WarpCommitted`s -//! - `VerifierInputs` — fresh root + l2 acc roots + opening proofs + answers -//! - `ReductionInputs` — `()` (no reduction) -//! - `ReducedStatement` — `()` (Proximity is a check, not a reduction) -//! - `ProofString` — opening proofs + shift_query_answers -//! - `ReducedWitness` — `()` -//! - `VerifierOutputs` — `()` - use ark_ff::Field; use ark_mt::MerkleHasher; use spongefish::{ProverState, VerifierState}; diff --git a/src/protocol/iors/sample_queries.rs b/src/protocol/iors/sample_queries.rs index 731fc1d..8b9f0bf 100644 --- a/src/protocol/iors/sample_queries.rs +++ b/src/protocol/iors/sample_queries.rs @@ -8,18 +8,6 @@ //! randomness, no prover messages, no oracles. Modeled as an IOR for //! uniformity with the rest of the choreography. //! -//! IOR signature -//! ------------- -//! - `Statement` — `(log_n, t)` -//! - `Witness` — `()` -//! - `ProverInputs` — `()` -//! - `VerifierInputs` — `()` -//! - `ReductionInputs` — the sampled [`QueryIndices`] -//! - `ReducedStatement` — same — both sides receive the queries -//! - `ProofString` — `()` -//! - `ReducedWitness` — `()` (no asymmetric data: both sides hold the same queries) -//! - `VerifierOutputs` — `()` - use ark_ff::Field; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; @@ -41,26 +29,10 @@ pub struct SampleQueriesReducedStatement { pub queries: QueryIndices, } -/// Shift-query sampling IOR configuration. Stateless. -pub struct SampleQueries<'a, F: Field> { - pub _phantom: PhantomData<&'a F>, -} - -impl<'a, F: Field> SampleQueries<'a, F> { - pub fn new() -> Self { - Self { - _phantom: PhantomData, - } - } -} - -impl<'a, F: Field> Default for SampleQueries<'a, F> { - fn default() -> Self { - Self::new() - } -} +#[derive(Default)] +pub struct SampleQueries(PhantomData); -impl<'a, F> IOR for SampleQueries<'a, F> +impl IOR for SampleQueries where F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { diff --git a/src/protocol/iors/twin_constraint.rs b/src/protocol/iors/twin_constraint.rs index ce9b7c9..815d3f6 100644 --- a/src/protocol/iors/twin_constraint.rs +++ b/src/protocol/iors/twin_constraint.rs @@ -1,35 +1,6 @@ -//! Twin-constraint sumcheck IOR. -//! -//! Paired spec: `docs/paper-mods/mod1_oracle.tex` (oracle composition). -//! The forthcoming `docs/paper-mods/mod2_structured_sumcheck.tex` will -//! promote this IOR's fused-fold prover to a first-class paper primitive. -//! -//! Reduces the claim -//! -//! ```text -//! Σ_i τ(i) · (f(i) + ω · p(i)) = σ₁ -//! ``` -//! -//! to evaluations at a random point γ via protogalaxy folding, where -//! - `f(X) = fold(α, oracle_evals)` — folded codeword check -//! - `p(X) = fold(β, Az·Bz − Cz)` — folded R1CS constraint check -//! - `t(X)` = linear interpolation of τ — equality polynomial -//! -//! Each round's round polynomial has the form `h(X) = (f(X) + ω·p(X))·t(X)`. -//! -//! IOR signature -//! ------------- -//! - `Statement` — accumulator instance + `l1_mus` + dimensions -//! - `Witness` — fresh witnesses + accumulator witness halves + R1CS -//! - `ProverInputs` — full codewords (from PESAT) + accumulated codewords -//! - `VerifierInputs` — `()` — the deferred oracle check (final\_claim ≟ eq(τ,γ)·(ν₀+ω·η)) -//! runs in the orchestrator after ν₀ and η arrive on the transcript -//! - `ReductionInputs` — `(ω, τ, γ, final_claim)` — both sides feed these into -//! `reduce_statement`, which computes ζ₀ / β_τ via `scale_and_sum` (single source of truth). -//! - `ReducedStatement` — sumcheck challenges γ + unchecked final_claim + ω, τ + new α (ζ₀) + new β_τ -//! - `ProofString` — `()` -//! - `ReducedWitness` — the new reduced oracle `f` + the reduced witness vector `z` -//! - `VerifierOutputs` — `()` (the new commitment is read from the transcript by the orchestrator) +//! Twin-constraint sumcheck IOR. Reduces `Σ_i τ(i) · (f(i) + ω·p(i)) = σ₁` +//! to evaluations at γ via protogalaxy folding. +//! Paired spec: `docs/paper-mods/mod1_oracle.tex`. use ark_ff::{Field, PrimeField}; use ark_mt::MerkleHasher; @@ -182,8 +153,6 @@ impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { } } -// ─── IOR signature types ────────────────────────────────────────────────── - pub struct TwinConstraintStatement { pub acc_instance: AccumulatorInstance, pub l1_mus: Vec, @@ -200,39 +169,19 @@ pub struct TwinConstraintWitness<'a, F: Field> { } pub struct TwinConstraintProverInputs<'a, F: Field> { - /// Fresh codewords emitted by PESAT. pub fresh_codewords: &'a [Vec], - /// Accumulated codewords (from the accumulator witness). pub acc_codewords: &'a [Vec], } -/// A typed "you owe me a check" handle. -/// -/// The TwinConstraint sumcheck reduces σ₁ to a sumcheck final value, but the -/// actual oracle check `final_claim ≟ eq(τ, γ) · (ν₀ + ω·η)` cannot be -/// completed inside `TwinConstraint::verify` because ν₀ and η arrive on the -/// transcript *after* the sumcheck rounds. Rather than splitting -/// TwinConstraint into two IORs (which cascades into other IORs having -/// similar shapes), we expose the obligation as a typed value. -/// -/// Discharge by calling [`Self::discharge`] with the missing inputs once the -/// orchestrator has read them from the transcript. +/// Deferred oracle check: `final_claim ≟ eq(τ,γ)·(ν₀ + ω·η)`. Cannot fire +/// inside `verify` because ν₀, η arrive on the transcript only after Bridge. pub struct DeferredOracleCheck { - /// Zero-check randomness ω squeezed at TwinConstraint entry. pub omega: F, - /// Zero-check challenge τ squeezed at TwinConstraint entry. pub tau: Vec, - /// Sumcheck final value — what the orchestrator must verify against - /// `eq(τ, γ) · (ν₀ + ω·η)`. pub claim: F, } impl DeferredOracleCheck { - /// Discharge the deferred check. - /// - /// Returns `Ok(())` iff `eq(τ, γ) · (ν₀ + ω·η) == claim`. `γ` arrives via - /// the parent `TwinConstraintReducedStatement`; `ν₀, η` come from the - /// transcript segment immediately following the TwinConstraint sumcheck. pub fn discharge(&self, gamma: &[F], nu_0: F, eta: F) -> Result<(), VerifierError> { let expected = eq_poly_non_binary(&self.tau, gamma) * (nu_0 + self.omega * eta); (expected == self.claim).then_some(()).ok_or(VerifierError::Target) @@ -240,40 +189,24 @@ impl DeferredOracleCheck { } pub struct TwinConstraintReducedStatement { - /// Sumcheck challenge vector (LSB-indexed). pub gamma: Vec, - /// New code-evaluation point (becomes the new accumulator's α). pub zeta_0: Vec, - /// Reduced τ point (becomes the τ component of the new accumulator's β). pub beta_tau: Vec, - /// Typed obligation: the sumcheck's final value awaits verification - /// against `(ν₀, η)` arriving on the transcript next. Call - /// [`DeferredOracleCheck::discharge`] from the orchestrator. pub deferred: DeferredOracleCheck, } -/// Inputs to [`TwinConstraint::reduce_statement`]. Both prover and -/// verifier produce this struct from their respective machinery and feed -/// it into the shared reduction. Drift between the two sides is -/// structurally impossible because ζ₀ / β_τ are computed in exactly one -/// place — `reduce_statement` — using `scale_and_sum`. pub struct TwinConstraintReductionInputs { pub omega: F, pub tau: Vec, - /// Sumcheck challenge vector (LSB-indexed). pub gamma: Vec, - /// Sumcheck final value (the unchecked claim). pub final_claim: F, } pub struct TwinConstraintReducedWitness { - /// New reduced codeword oracle. pub f: Oracle, - /// Reduced witness vector `z = (x, w)` — consumed by η evaluation. pub z: Vec, } -/// TwinConstraint IOR configuration. pub struct TwinConstraint<'a, F, H> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, diff --git a/src/protocol/oracles/evaluation.rs b/src/protocol/oracles/evaluation.rs index 928b492..3c55b4e 100644 --- a/src/protocol/oracles/evaluation.rs +++ b/src/protocol/oracles/evaluation.rs @@ -1,19 +1,5 @@ -//! Oracle abstraction for Warp's IORs. -//! -//! Paired spec: `docs/paper-mods/mod1_oracle.tex`. -//! -//! An [`Oracle`] carries both views Warp's IORs need of a committed -//! codeword: the raw evaluation table `f: [n] → F` (BCS-native, -//! index-queryable) and the implied multilinear extension -//! `\hat f: F^{log n} → F` (point-queryable). The multilinear extension is -//! materialised lazily on first point query and cached. -//! -//! The Merkle commitment of the codeword is **not** held here. In PESAT a -//! single Merkle tree covers many interleaved codewords (see -//! `src/crypto/merkle/mod.rs::build_codeword_leaves`), so the tree is -//! tracked by the enclosing data structure (`AccumulatorWitness`, the -//! PESAT reduced witness) rather than 1:1 with the oracle. See the -//! Implementation note in `mod1_oracle.tex` §2. +//! Codeword oracle: index-queryable evaluation table plus its +//! lazily-materialised multilinear extension. use ark_ff::Field; use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; @@ -30,7 +16,6 @@ pub struct Oracle { } impl Oracle { - /// Wrap an existing evaluation table. pub fn from_evals(evals: Vec) -> Self { Self { evals, @@ -38,17 +23,14 @@ impl Oracle { } } - /// Borrow the evaluation table `f`. pub fn evals(&self) -> &[F] { &self.evals } - /// Consume the oracle and return the underlying evaluation table. pub fn into_evals(self) -> Vec { self.evals } - /// Length `n` of the evaluation table. pub fn len(&self) -> usize { self.evals.len() } @@ -57,14 +39,12 @@ impl Oracle { self.evals.is_empty() } - /// Index query: `f[i]`. pub fn query_at_leaf(&self, idx: usize) -> F { count_ops!(OracleLeafQueries); self.evals[idx] } - /// Point query on the multilinear extension: `\hat f(ζ)` for - /// `ζ ∈ F^{log n}`. Materialises the MLE on first call and caches it. + /// `\hat f(ζ)`. Materialises the MLE on first call, caches afterward. pub fn query_at_point(&self, point: &[F]) -> F { count_ops!(OraclePointQueries); let mle = self.mle.get_or_init(|| { diff --git a/src/protocol/oracles/indexed_merkle.rs b/src/protocol/oracles/indexed_merkle.rs index fcfb646..6ee5ea6 100644 --- a/src/protocol/oracles/indexed_merkle.rs +++ b/src/protocol/oracles/indexed_merkle.rs @@ -1,23 +1,5 @@ -//! Oracle access via handle traits. -//! -//! IOR verifiers code against [`IndexedOracle`] rather than against a -//! concrete BCS-shaped tuple of `(root, opening proof, precomputed answer -//! table)`. The handle internally validates the commitment-scheme opening -//! and exposes a partial-function view of the committed oracle: -//! -//! ```text -//! query(i) -> Some(value) iff index in range AND opening passes -//! query(i) -> None otherwise -//! ``` -//! -//! Matches the IOP/BCS formalism where oracles are partial functions; -//! the BCS instantiation (Merkle root + path-pruned multi-opening proof + -//! authenticated values) lives behind the trait, not in IOR verifier code. -//! -//! Currently used by [`super::proximity`] on the verifier side. Other -//! IORs continue to use concrete oracle types for now; migration of -//! OOD / Batching prover-side `Oracle` access to a sibling -//! `EvalOracle` trait is tracked as a follow-up. +//! Partial-function view of a Merkle-committed indexed oracle: +//! `query(i)` returns `Some(value)` iff the opening at `i` validates. use std::cell::OnceCell; @@ -26,28 +8,13 @@ use ark_mt::{multi_vector::MultiVectorOpening, MerkleHasher}; use crate::crypto::merkle::{WarpProof, WarpScheme}; -/// Partial-function view of an indexed oracle. -/// -/// Implementations are responsible for any validation needed before -/// returning a value. `query(i) -> None` covers both "index out of -/// range" and "opening failed validation." pub trait IndexedOracle { fn query(&self, i: usize) -> Option; - /// Optional eager validation hook. Callers that want a single - /// up-front check (rather than lazy per-query) call `validate()` - /// once. The default impl performs a no-op `query(0)` to drive - /// whatever lazy validation the impl uses. fn validate(&self) -> bool { self.query(0).is_some() } } -/// BCS-checked Merkle handle for a multi-vector commitment. -/// -/// Holds the scheme, root, opening proof, and the (sorted, unique) -/// indices + per-index values that get authenticated against the root. -/// Validation runs lazily on the first call to [`query`] / -/// [`validate`] and is memoized so repeated calls are free. pub struct MerkleIndexedOracle<'a, F, H> where F: Field + Clone, @@ -56,12 +23,8 @@ where pub scheme: WarpScheme, pub root: &'a H::Digest, pub proof: &'a WarpProof, - /// Sorted, unique leaf positions that appear in `values_by_index`. pub sorted_indices: Vec, - /// `values_by_index[k]` is the leaf-tuple at `sorted_indices[k]`. - /// Inner length = number of codewords interleaved under this root. pub values_by_index: Vec>, - /// Memoized opening-validation result. validated: OnceCell, } diff --git a/src/protocol/oracles/query_indices.rs b/src/protocol/oracles/query_indices.rs index bbe207f..b4ca8f6 100644 --- a/src/protocol/oracles/query_indices.rs +++ b/src/protocol/oracles/query_indices.rs @@ -3,12 +3,11 @@ use spongefish::ProverState; #[derive(Clone)] pub struct QueryIndices { - pub leaf_positions: Vec, // for merkle tree lookups - pub evaluation_points: Vec>, // for eq polynomial evals + pub leaf_positions: Vec, + pub evaluation_points: Vec>, } impl QueryIndices { - // take the prover state and sample for queries pub fn sample( prover_state: &mut ProverState, log_codeword_len: usize, @@ -23,11 +22,9 @@ impl QueryIndices { Self::from_squeezed_bytes(&squeezed_bytes, log_codeword_len, num_queries) } - // format the queries from squeezed bytes pub fn from_squeezed_bytes(squeezed_bytes: &[u8], log_n: usize, count: usize) -> Self { let evaluation_points = Self::evaluation_points_from_squeezed_bytes(squeezed_bytes, log_n, count); - // Compute all leaf positions in one batch let leaf_positions = Self::leaf_positions_from_evaluation_points(&evaluation_points); Self { leaf_positions, @@ -35,7 +32,6 @@ impl QueryIndices { } } - // Get Vec of len=num_queries, where each elements is vec of F in {0,1} len=log_codeword_len fn evaluation_points_from_squeezed_bytes( squeezed_bytes: &[u8], log_codeword_len: usize, diff --git a/src/warp/accumulator.rs b/src/warp/accumulator.rs index 8a430a5..35cbc50 100644 --- a/src/warp/accumulator.rs +++ b/src/warp/accumulator.rs @@ -24,14 +24,6 @@ impl AccumulatorInstance { } } - pub fn len(&self) -> usize { - self.rt.len() - } - - pub fn is_empty(&self) -> bool { - self.rt.is_empty() - } - pub fn extend(mut self, other: Self) -> Self { self.rt.extend(other.rt); self.alpha.extend(other.alpha); @@ -44,7 +36,6 @@ impl AccumulatorInstance { } /// Private part of an accumulated claim: `(td, w)` in the paper. -/// Codewords live inside `td[i].codewords()`. pub struct AccumulatorWitness where F: Field, @@ -80,14 +71,6 @@ where } } - pub fn len(&self) -> usize { - self.td.len() - } - - pub fn is_empty(&self) -> bool { - self.td.is_empty() - } - pub fn extend(mut self, other: Self) -> Self { self.td.extend(other.td); self.w.extend(other.w); diff --git a/src/warp/decide.rs b/src/warp/decide.rs index 43f213e..7bd5101 100644 --- a/src/warp/decide.rs +++ b/src/warp/decide.rs @@ -20,7 +20,7 @@ where H: MerkleHasher>, H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, { - pub(crate) fn decide_impl( + pub fn decide( &self, acc_witness: AccumulatorWitness, acc_instance: AccumulatorInstance, diff --git a/src/warp/prove.rs b/src/warp/prove.rs index ee08c15..3b05cae 100644 --- a/src/warp/prove.rs +++ b/src/warp/prove.rs @@ -42,7 +42,7 @@ where H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, { #[tracing::instrument(name = "warp.prove", skip_all)] - pub(crate) fn prove_impl( + pub fn prove( &self, pk: WARPProverKey

    , prover_state: &mut ProverState, @@ -107,16 +107,15 @@ where r1cs: self.params.p.constraints(), _phantom: PhantomData, }; - let bridge_ior = Bridge::::new(); - let ood_ior = Ood::::new(); - let sample_queries_ior = SampleQueries::::new(); - let batching_ior = Batching::::new(); + let bridge_ior = Bridge::::default(); + let ood_ior = Ood::::default(); + let sample_queries_ior = SampleQueries::::default(); + let batching_ior = Batching::::default(); let proximity_ior = Proximity:: { hasher: &self.params.hasher, _phantom: PhantomData, }; - // ── IOR 1: PESAT ───────────────────────────────────────────── let IorProveResult { reduced: PesatReducedStatement { mus, taus }, proof: _, @@ -129,7 +128,6 @@ where inputs: (), )?; - // ── IOR 2: TwinConstraint ──────────────────────────────────── let IorProveResult { reduced: TwinConstraintReducedStatement { gamma: _, @@ -161,7 +159,6 @@ where }, )?; - // ── IOR 3: Bridge ──────────────────────────────────────────── let IorProveResult { reduced: BridgeReducedStatement { eta, @@ -188,7 +185,6 @@ where }, )?; - // ── IOR 4: OOD ─────────────────────────────────────────────── let IorProveResult { reduced: OodReducedStatement { samples_flat, answers }, proof: _, @@ -201,7 +197,6 @@ where inputs: OodProverInputs { oracle: &f }, )?; - // ── IOR 5: SampleQueries ───────────────────────────────────── let IorProveResult { reduced: SampleQueriesReducedStatement { queries }, proof: _, @@ -214,7 +209,6 @@ where inputs: (), )?; - // ── IOR 6: Batching ────────────────────────────────────────── let IorProveResult { reduced: BatchingReducedStatement { alpha }, proof: _, @@ -234,7 +228,6 @@ where inputs: BatchingProverInputs { oracle: &f }, )?; - // ── IOR 7: Proximity ───────────────────────────────────────── let IorProveResult { reduced: _, proof: ProximityProofString { diff --git a/src/warp/scheme.rs b/src/warp/scheme.rs index 5aa16b9..2f653f5 100644 --- a/src/warp/scheme.rs +++ b/src/warp/scheme.rs @@ -3,18 +3,13 @@ use ark_ff::{Field, PrimeField}; use ark_mt::MerkleHasher; use spongefish::{ Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerificationResult, - VerifierState, }; use std::marker::PhantomData; -use crate::accumulation::AccumulationScheme; use crate::config::WARPConfig; -use crate::error::{VerifierError, WARPError}; -use crate::relations::{r1cs::R1CSConstraints, BundledPESAT}; -use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; +use crate::relations::BundledPESAT; use crate::warp::keys::{WARPProverKey, WARPVerifierKey}; use crate::warp::params::WARPParams; -use crate::warp::proof::{ProveResult, WARPProof}; pub struct WARP, C: LinearCode + Clone, H: MerkleHasher> { pub params: WARPParams, @@ -40,24 +35,17 @@ where } } -impl AccumulationScheme for WARP +impl WARP where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - P: Clone + BundledPESAT, Config = (usize, usize, usize)>, + P: Clone + BundledPESAT, C: LinearCode + Clone, H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, { - type Index = P; - type ProverKey = WARPProverKey

    ; - type VerifierKey = WARPVerifierKey; - type Instances = Vec>; - type Witnesses = Vec>; - - fn index( + pub fn index( prover_state: &mut ProverState, - index: Self::Index, - ) -> VerificationResult<(Self::ProverKey, Self::VerifierKey)> { + index: P, + ) -> VerificationResult<(WARPProverKey

    , WARPVerifierKey)> { let (m, n, k) = index.config(); prover_state.public_message(&index.description()); prover_state.prover_message(&F::from(m as u32)); @@ -68,34 +56,4 @@ where WARPVerifierKey { m, n, k }, )) } - - fn prove( - &self, - pk: Self::ProverKey, - prover_state: &mut ProverState, - witnesses: Self::Witnesses, - instances: Self::Instances, - acc_instance: AccumulatorInstance, - acc_witness: AccumulatorWitness, - ) -> ProveResult { - self.prove_impl(pk, prover_state, witnesses, instances, acc_instance, acc_witness) - } - - fn verify<'a>( - &self, - vk: Self::VerifierKey, - verifier_state: &mut VerifierState<'a>, - acc_instance: AccumulatorInstance, - proof: WARPProof, - ) -> Result<(), VerifierError> { - self.verify_impl(vk, verifier_state, acc_instance, proof) - } - - fn decide( - &self, - acc_witness: AccumulatorWitness, - acc_instance: AccumulatorInstance, - ) -> Result<(), WARPError> { - self.decide_impl(acc_witness, acc_instance) - } } diff --git a/src/warp/verify.rs b/src/warp/verify.rs index 36f9fa9..ff7f9cc 100644 --- a/src/warp/verify.rs +++ b/src/warp/verify.rs @@ -38,7 +38,7 @@ where H: MerkleHasher>, H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, { - pub(crate) fn verify_impl<'a>( + pub fn verify<'a>( &self, vk: WARPVerifierKey, verifier_state: &mut VerifierState<'a>, @@ -74,16 +74,15 @@ where r1cs: self.params.p.constraints(), _phantom: PhantomData, }; - let bridge_ior = Bridge::::new(); - let ood_ior = Ood::::new(); - let sample_queries_ior = SampleQueries::::new(); - let batching_ior = Batching::::new(); + let bridge_ior = Bridge::::default(); + let ood_ior = Ood::::default(); + let sample_queries_ior = SampleQueries::::default(); + let batching_ior = Batching::::default(); let proximity_ior = Proximity:: { hasher: &self.params.hasher, _phantom: PhantomData, }; - // ── IOR 1: PESAT ───────────────────────────────────────────── let IorVerifyResult { reduced: PesatReducedStatement { mus: l1_mus, @@ -97,7 +96,6 @@ where inputs: (), )?; - // ── IOR 2: TwinConstraint ──────────────────────────────────── let IorVerifyResult { reduced: TwinConstraintReducedStatement { gamma, @@ -120,7 +118,6 @@ where inputs: (), )?; - // ── IOR 3: Bridge ──────────────────────────────────────────── let IorVerifyResult { reduced: BridgeReducedStatement { eta: _, @@ -143,7 +140,6 @@ where }, )?; - // ── IOR 4: OOD ─────────────────────────────────────────────── let IorVerifyResult { reduced: OodReducedStatement { samples_flat, answers }, outputs: _, @@ -154,7 +150,6 @@ where inputs: (), )?; - // ── IOR 5: SampleQueries ───────────────────────────────────── let IorVerifyResult { reduced: SampleQueriesReducedStatement { queries }, outputs: _, @@ -212,7 +207,6 @@ where }) .collect(); - // ── IOR 7: Proximity ───────────────────────────────────────── verify_ior!( proximity_ior, verifier_state, @@ -241,7 +235,6 @@ where nus.push(nu_st); } - // ── IOR 6: Batching ────────────────────────────────────────── let IorVerifyResult { reduced: BatchingReducedStatement { alpha }, outputs: _, diff --git a/tests/integration_warp.rs b/tests/integration_warp.rs index e62e27c..e55917b 100644 --- a/tests/integration_warp.rs +++ b/tests/integration_warp.rs @@ -32,7 +32,6 @@ use warp::relations::{ BundledPESAT, Relation, ToPolySystem, }; use warp::serialize::acc_witness_size; -use warp::accumulation::AccumulationScheme; use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProverKey, WARPVerifierKey}; use warp::utils::poseidon; use warp::WARP; diff --git a/tests/profile_json.rs b/tests/profile_json.rs index 6ea46a9..ac35b01 100644 --- a/tests/profile_json.rs +++ b/tests/profile_json.rs @@ -27,7 +27,6 @@ use warp::relations::{ }, BundledPESAT, Relation, ToPolySystem, }; -use warp::accumulation::AccumulationScheme; use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProverKey}; use warp::utils::poseidon; use warp::WARP; diff --git a/tests/verifier_negative.rs b/tests/verifier_negative.rs index 3757c5a..abe850a 100644 --- a/tests/verifier_negative.rs +++ b/tests/verifier_negative.rs @@ -39,7 +39,6 @@ use warp::relations::{ }, BundledPESAT, Relation, ToPolySystem, }; -use warp::accumulation::AccumulationScheme; use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProof, WARPProverKey, WARPVerifierKey}; use warp::utils::poseidon; use warp::WARP; From d25bbc0ffb9f4454f53d8650c16a1c5d5fd544b9 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 12 May 2026 16:55:24 +0200 Subject: [PATCH 60/68] more cleanup --- docs/paper-mods/slides/iors.pdf | Bin 0 -> 325389 bytes docs/paper-mods/slides/iors.tex | 311 ++++++++++++++++++++++++++++++++ 2 files changed, 311 insertions(+) create mode 100644 docs/paper-mods/slides/iors.pdf create mode 100644 docs/paper-mods/slides/iors.tex diff --git a/docs/paper-mods/slides/iors.pdf b/docs/paper-mods/slides/iors.pdf new file mode 100644 index 0000000000000000000000000000000000000000..254f8090463d15925449628f39c99dbc7229f7ab GIT binary patch literal 325389 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

    F80y~S{nM^OL#v!ID0@mU#{*H<~Uj5%^i9F&y< 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 H=YOswP_3J81Fc5bDf8H*)j7kmL5w2yv4r~T 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 literal 0 HcmV?d00001 diff --git a/docs/paper-mods/slides/iors.tex b/docs/paper-mods/slides/iors.tex new file mode 100644 index 0000000..4508c77 --- /dev/null +++ b/docs/paper-mods/slides/iors.tex @@ -0,0 +1,311 @@ +\documentclass[10pt,aspectratio=169]{beamer} + +\usepackage{listings} +\usepackage{xcolor} +\usepackage{booktabs} +\usepackage{array} + +\usetheme{metropolis} +\usepackage[scale=0.85]{FiraSans} + +\definecolor{warpaccent}{HTML}{2C3E50} +\definecolor{warpcode}{HTML}{1B5E20} +\definecolor{warpgray}{HTML}{555555} + +\setbeamercolor{frametitle}{bg=warpaccent} +\setbeamercolor{title separator}{fg=warpaccent} +\setbeamertemplate{frame numbering}[fraction] + +\lstset{ + basicstyle=\ttfamily\footnotesize, + keywordstyle=\color{warpaccent}\bfseries, + commentstyle=\color{warpgray}\itshape, + stringstyle=\color{warpcode}, + showstringspaces=false, + morekeywords={Self, dyn, impl, async, await, fn, type, trait, where, Result, Vec, Box, Option, mut, pub, struct, let, return, use}, + keepspaces=true, + columns=fullflexible, + frame=none, + breaklines=true, +} + +\title{Warp Modularity} +\subtitle{From paper IORs to a code-level interface} +\author{Internal note} +\date{} + +\begin{document} + +\maketitle + +% -------------------------------------------------------------------- +\begin{frame}{What we want} +A single interface every Warp phase satisfies, that is visibly the same +shape as the paper's IOR signature. + +\vspace{0.5em} +\begin{itemize} + \item \texttt{lib.rs::prove} reads as a chain of typed transitions. + \item Each transition: $(\text{stmt}, \text{wit}, \text{or}_\text{in}) \mapsto (\text{stmt}', \text{or}_\text{out})$. + \item Adding a phase: a struct + a trait impl. No bespoke plumbing. +\end{itemize} + +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{Why Warp wasn't easy modularly} +The paper IOR formalism doesn't share state across IORs. Real Warp does. + +\vspace{0.4em} +\textbf{Three concrete frictions:} +\begin{enumerate} + \item \textbf{Shared oracle $f$.} TwinConstraint, OOD, Batching, Proximity all consume the same codeword. A literal IOR-per-phase would rematerialise. + \item \textbf{Transcript ordering coupling.} Shift-query indices are squeezed once and consumed by both Batching and Proximity. No phase ``owns'' the squeeze. + \item \textbf{Statement / witness / oracle conflation.} The pre-refactor function had $\sim$10 mixed-purpose args; the paper's tripartite split was invisible. +\end{enumerate} +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{What an IOR signature is} +The Warp paper structures the protocol as a sequence of Interactive Oracle Reductions, each one having the composition rule: +\[ A: (\mathrm{stmt}_A, \mathrm{wit}_A) \mapsto (\mathrm{stmt}_A', \mathsf{O}[f]) \] +\[ B: (\mathrm{stmt}_B, \mathsf{O}[f]) \mapsto \mathrm{stmt}_B' \] +\[ A;B: (\mathrm{stmt}_A, \mathrm{stmt}_B, \mathrm{wit}_A) \mapsto (\mathrm{stmt}_A', \mathrm{stmt}_B') \] + +\vspace{0.4em} +Five typed components per IOR: + +\begin{center}\small +\begin{tabular}{lcc} +\toprule +& \textbf{Prover} & \textbf{Verifier} \\ +\midrule +\texttt{Statement} & \checkmark & \checkmark \\ +\texttt{Witness} & \checkmark & --- \\ +\texttt{InputOracles} & by data & by handle \\ +\texttt{ReducedStatement} & \checkmark & \checkmark \\ +\texttt{OutputOracles} & by data & by handle \\ +\bottomrule +\end{tabular} +\end{center} +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}[fragile]{The IOR trait we built} +\small +Current shape (post-PR\#22 review) mirrors the paper and routes both sides through one reduction function: +\begin{lstlisting} +pub trait IOR { + type Statement; type Witness; + type ProverInputs; type VerifierInputs; + type ReductionInputs; + type ReducedStatement; + type ProofString; type ReducedWitness; + type VerifierOutputs; + + fn reduce_statement(&self, &Stmt, &ReductionInputs) -> Reduced; + fn prove_inner(...) -> (ReductionInputs, ProofString, ReducedWitness); + fn verify_inner(...) -> (ReductionInputs, VerifierOutputs); + // default `prove` / `verify` chain *_inner -> reduce_statement +} +\end{lstlisting} +Implementor writes the three; defaults wire them. \texttt{Statement} / \texttt{ReducedStatement} shared; oracle types role-split; reduction is single-source-of-truth. 33/33 tests green. +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{Per-phase mapping} +\scriptsize +\begin{center} +\begin{tabular}{lp{1.2cm}p{1.0cm}p{1.0cm}p{1.4cm}p{0.9cm}p{1.0cm}} +\toprule +& \textbf{Stmt} & \textbf{Wit} & \textbf{Pr.\,In} & \textbf{Reduced} & \textbf{Proof} & \textbf{Red.\,Wit} \\ +\midrule +\texttt{Pesat} & $(l_1, \log m)$ & wits & --- & $(\mu, \tau)$ & --- & cw + tree \\ +\texttt{TwinC.} & acc + $\mu$/$\tau$ & acc-w + ins & cw + acc-cw & $\gamma, \zeta_0, \beta_\tau,$ defer & --- & $\mathsf{O}[f] + z$ \\ +\texttt{Ood} & $(s, \log n)$ & --- & $\mathsf{O}[f]$ & samples + ans & --- & --- \\ +\texttt{Batching} & $\zeta$\,+\,$s,t,\log n$ & --- & $\mathsf{O}[f]$ & $\alpha$ & --- & $\mu$ \\ +\texttt{Proximity} & queries\,+\,$l_2,t$ & --- & trees + cw & $()$ & paths + ans & --- \\ +\bottomrule +\end{tabular} +\end{center} + +\vspace{0.4em} +\textbf{Asymmetries are now structural:} +\begin{itemize}\scriptsize + \item \texttt{Pesat} \,---\, empty \texttt{ProverInputs} (it's the source). + \item \texttt{Proximity} \,---\, empty \texttt{Reduced} and \texttt{Red.\,Wit} (it's a check; pure \texttt{ProofString}). + \item \texttt{Ood} / \texttt{Batching} \,---\, empty \texttt{Witness}. + \item \texttt{ProofString} is non-empty only for \texttt{Proximity}; every other phase's prover output is reduced-witness-shaped. +\end{itemize} +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{Single-protocol view: a candid review} +\small +\textbf{What's good.} +\begin{itemize}\small + \item Paper-aligned types \,---\, the \texttt{IOR} trait and paper composition rule line up directly. + \item Verifier symmetry \,---\, \texttt{*::verify} are first-class trait impls. + \item Honest oracle role split \,---\, prover sees data, verifier sees \emph{handles} (see oracle slide). + \item Empty-type asymmetries visible (\texttt{ProofString = ()} for reductions; \texttt{Reduced = ()} for checks). + \item Statement reduction is single-source-of-truth \,---\, no prover/verifier drift. +\end{itemize} + +\vspace{0.4em} +\textbf{What's not.} +\begin{itemize}\small + \item Nine associated types per phase \,---\, real cognitive load. + \item Lifetimes \& \texttt{PhantomData} proliferate; structs carry \texttt{<'a, F, \dots>}. + \item No type-level ordering enforcement; transcript order is implicit. + \item Orchestrator got longer, not shorter \,---\, $\sim$10 lines per phase, plus oracle-handle construction. +\end{itemize} +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{Reframing: a family of protocols} +The single-protocol view above was honest \,---\, but the actual goal isn't ``a clean WARP.'' + +\vspace{0.4em} +We design protocols like \textbf{STIR}, \textbf{WHIR}, \textbf{WARP} all the time. They share: +\begin{itemize}\small + \item Encode-and-commit (codeword \,---\, Reed-Solomon, or otherwise). + \item Sumcheck reductions (different flavours: degree-1, multilinear, fused twin-constraint, $\dots$). + \item Out-of-domain (OOD) sampling for proximity boost. + \item Shift-query opening with auth paths. + \item Batching sumchecks that fold many claims into one. + \item Accumulator updates / final consistency. +\end{itemize} + +\vspace{0.4em} +\textbf{The bar for the IOR trait then shifts:} not ``does this clean up WARP?'' but ``is this the right backbone for a family?'' +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{The toolkit, audited} +What practitioners writing STIR / WHIR / WARP \emph{already} have: +\begin{itemize}\small + \item \textbf{Transcripts.} \texttt{spongefish} 0.7 \,---\, de facto in this corner of the ecosystem. Practitioners use it directly. No abstraction needed. + \item \textbf{Sumchecks.} \texttt{effsc} \,---\, \texttt{SumcheckProver}, \texttt{RoundPolyEvaluator}, \texttt{InnerProductProver}, \texttt{CoefficientProverLSB}, \texttt{MultilinearProver}, hypercube + protogalaxy utilities. WARP uses 9 of 10 effsc primitives already. + \item \textbf{Linear codes.} \texttt{ark-codes::LinearCode} \,---\, encode, code\_len. + \item \textbf{Vector commitments.} \texttt{ark-vc} (abstract trait) + \texttt{ark-mt} (Merkle backend, multi-vector + single-shot openings, Blake3 / Poseidon hashers). + \item \textbf{Oracle struct.} \texttt{Vec} codeword + lazy MLE cache. Same storage shape across STIR / WHIR / WARP. +\end{itemize} + +\vspace{0.4em} +The primitive layer is mostly done. The discussion is really about \emph{integration} \,---\, how primitives compose, and what shape protocol authors copy. +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{What practitioner experience actually needs} +Designing a STIR / WHIR / WARP-style protocol is hard. The friction is rarely ``I need a new sumcheck primitive.'' It's: +\begin{itemize}\small + \item \textbf{Tying primitives together correctly.} LinearCode + Merkle is two crates; integration is per-protocol boilerplate. + \item \textbf{Knowing the right phase decomposition.} What's a phase? What's its statement? Where's the witness? When does the verifier act? + \item \textbf{Threading transcript ordering without bugs.} Squeeze before write, derive challenges in the right segments. + \item \textbf{Maintaining paper-code alignment.} The paper's IOR composition is the contract; reviewers expect to see it in code. + \item \textbf{Debugging when soundness fails.} ``Where in this 600-line \texttt{verify} did the rejection happen?'' +\end{itemize} + +\vspace{0.4em} +The fellow: \emph{practitioners benefit from patterns + examples + shared primitives}, not from cross-protocol trait contracts. That's the lever. +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}[fragile]{What WARP migrated onto: \texttt{ark-vc} / \texttt{ark-mt}} +\small +Replaced \texttt{ark-crypto-primitives::merkle\_tree} with a multi-vector commitment scheme. Two structural changes: + +\vspace{0.3em} +\textbf{1. Per-codeword $\to$ one tree.} PESAT commits $l_1$ fresh codewords; leaves are column-tuples $(c_0[i], \dots, c_{l_1-1}[i])$, hashed as \texttt{Vec}. One root authenticates all codewords. + +\vspace{0.3em} +\textbf{2. Per-query $\to$ one proof.} Was $t$ separate \texttt{Path}, each carrying $\log n$ siblings. Now a single path-pruned \texttt{OpeningProof} for all $t$ positions (union of copaths minus union of paths, book §20). + +\vspace{0.3em} +At $n{=}2^{15}$, $t{=}100$: $1500 \to \approx 730$ sibling hashes. Savings grow with $t$. + +\vspace{0.3em} +\textbf{Type diff:} \texttt{MerkleTree} $\to$ \texttt{MultiVectorCommitted}; \texttt{Path} $\to$ \texttt{OpeningProof>}; \texttt{H: MerkleHasher>} replaces \texttt{MT: Config} across all phases. +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{What we're for, positively} +\textbf{Keep \texttt{IOR} as a structural template, not a cross-protocol contract.} +\begin{itemize}\small + \item It's the shape WARP-style protocols copy. The paper section on accumulator-as-state makes it legible. + \item Other protocols follow as a \emph{pattern}, get shared mental model + idioms, without being forced into one trait. +\end{itemize} + +\textbf{Lean on the existing primitive layer.} +\begin{itemize}\small + \item \texttt{effsc}, \texttt{spongefish}, \texttt{ark-codes}, \texttt{ark-vc} + \texttt{ark-mt}. + \item Already strong. The discipline is to use them, not reinvent. +\end{itemize} + +\textbf{WARP runs on \texttt{ark-vc}.} The missing integration trait was filled by \texttt{ark-vc}/\texttt{ark-mt}; WARP migrated onto its multi-vector commitment scheme. STIR-lite plugs into the same surface. + +\textbf{Make WARP the reference implementation.} A new protocol designer reads \texttt{src/protocol/phases/}, copies the IOR-shaped pattern, plugs in their primitives. + +\textbf{Document the pattern.} Short ``how to design a Warp-style protocol'' guide \,---\, narrative, not a trait. The actual lever for practitioner ergonomics. +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}[fragile]{\texttt{reduce\_statement} \,---\, eliminating prover/verifier drift (1 of 2)} +\small +\textbf{Problem.}\; +Prover and verifier each compute \texttt{ReducedStatement} via parallel-but-different code; they produce the same value by sumcheck-fold semantics, nothing structural keeps them in sync. Concrete site: TwinConstraint's $\zeta_0$ / $\beta_\tau$ \,---\, prover pulls from sumcheck CC's internal \texttt{tablewise()}, verifier recomputes via \texttt{scale\_and\_sum}. Patch one, forget the other, ship divergence. + +\vspace{0.3em} +\textbf{Change.} +\begin{lstlisting} +fn reduce_statement(&self, &Stmt, &ReductionInputs) -> ReducedStatement; +fn prove_inner(...) -> (ReductionInputs, ProofString, ReducedWitness); +fn verify_inner(...) -> (ReductionInputs, VerifierOutputs); +// default `prove`/`verify` chain *_inner -> reduce_statement. +\end{lstlisting} +Both sides land at \texttt{reduce\_statement} with the same \texttt{ReductionInputs}; the math runs in one place. + +\vspace{0.3em} +\textbf{Outcome.} Drift is structurally impossible: prover pulls only $f$, $z$ from CC; verifier stops parallel \texttt{scale\_and\_sum}. Cost: 1 associated type + 2 methods. 33/33 tests green. +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}[fragile]{\texttt{IndexedOracle} \,---\, BCS-agnostic verifier-side oracles (2 of 2)} +\small +\textbf{Problem.}\; +\texttt{ProximityVerifierInputs} carried \texttt{\{ rt\_0, l2\_roots, auth\_0, auth\_j, shift\_query\_answers \}} \,---\, a fully materialised BCS instantiation glued onto the IOR-level type. Slide 4 promises ``by handle''; the code delivered ``by precomputed table plus auth artifacts.'' Phase verifier knew about Merkle paths. + +\vspace{0.3em} +\textbf{Change.} +\begin{lstlisting} +trait IndexedOracle { fn query(&self, i: usize) -> Option; ... } +struct MerkleIndexedOracle<'a, F, H> { /* root, proof, idx+vals, OnceCell */ } +impl IndexedOracle> for MerkleIndexedOracle<...> { ... } +\end{lstlisting} +\texttt{ProximityVerifierInputs} is now generic over \texttt{O: IndexedOracle>} and holds \texttt{\&O} / \texttt{\&[O]} (static dispatch, no trait objects). BCS handle construction moves to the orchestrator. + +\vspace{0.3em} +\textbf{Outcome.} Phase code is BCS-agnostic \,---\, two \texttt{validate()} calls in \texttt{verify\_inner}. iBCS / ARG composition slots in via a different impl. Lazy validation \texttt{OnceCell}-memoised, so perf matches the prior eager check. + +\vspace{0.3em} +\textbf{Caveat.} Prover-side \texttt{Oracle} (OOD / Batching) is still concrete; sibling \texttt{EvalOracle} for point queries is a follow-up. +\end{frame} + +% -------------------------------------------------------------------- +\begin{frame}{Where this leaves us \,---\, the next move} +\small +\textbf{Already landed.}\; +\texttt{ark-vc}/\texttt{ark-mt} integration ($\sim$16\% smaller proof, prover matches with \texttt{parallel}). Four PR-\#22 follow-ups: \texttt{\&}-borrow, \texttt{ProofString}/\texttt{ReducedWitness} split, \texttt{IndexedOracle} handle, \texttt{reduce\_statement} split. Tests + benches green. + +\vspace{0.5em} +\textbf{Next.} +\begin{itemize}\small + \item Paper section \,---\, accumulator as typed state: IOR trait as paper-aligned \emph{template} for WARP-family protocols. + \item Paper section \,---\, structured sumcheck primitive: TwinConstraint's fused $\alpha/\beta/\tau$-fold extracted into \texttt{effsc} as a citable \texttt{RoundPolyEvaluator} impl. + \item Sibling \texttt{EvalOracle} \,---\, point-query analogue of \texttt{IndexedOracle} so prover-side OOD / Batching is BCS-agnostic too. + \item Second protocol \,---\, STIR-lite or WHIR-lite against the same primitives. Friction it surfaces is the next iteration's guide. + \item Practitioner narrative \,---\, ``how to design a Warp-style protocol,'' \texttt{docs/}. +\end{itemize} +\end{frame} + +\end{document} From 779f868b35ff41371ad448a2ea1beaeafacef829 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 12 May 2026 16:58:29 +0200 Subject: [PATCH 61/68] fmt --- benches/warp_rs.rs | 7 +++- src/protocol/ior.rs | 7 ++-- src/protocol/iors/batching.rs | 6 ++-- src/protocol/iors/bridge.rs | 4 +-- src/protocol/iors/mod.rs | 8 ++--- src/protocol/iors/ood.rs | 2 +- src/protocol/iors/proximity.rs | 22 +++++++----- src/protocol/iors/twin_constraint.rs | 8 +++-- src/warp/prove.rs | 50 +++++++++++++++++----------- src/warp/verify.rs | 43 +++++++++++++----------- tests/integration_warp.rs | 47 +++++++++++++++++++++----- tests/profile_json.rs | 9 +++-- tests/verifier_negative.rs | 39 ++++++++++++++++------ 13 files changed, 167 insertions(+), 85 deletions(-) diff --git a/benches/warp_rs.rs b/benches/warp_rs.rs index d1aad00..08f65c1 100644 --- a/benches/warp_rs.rs +++ b/benches/warp_rs.rs @@ -56,7 +56,12 @@ pub fn bench_rs_warp_fields(c: &mut Criterion) { |(mut prover_state, _x_w)| { let _ = hash_chain_warp .prove( - WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, + WARPProverKey { + index: r1cs.clone(), + m: r1cs.m, + n: r1cs.n, + k: r1cs.k, + }, &mut prover_state, instances_witnesses.1.clone(), instances_witnesses.0.clone(), diff --git a/src/protocol/ior.rs b/src/protocol/ior.rs index 8118f56..1dcd9f8 100644 --- a/src/protocol/ior.rs +++ b/src/protocol/ior.rs @@ -1,8 +1,6 @@ //! The Interactive Oracle Reduction abstraction. Paired spec: //! `docs/paper-mods/mod1_oracle.tex`. - - use spongefish::{ProverState, VerifierState}; use crate::error::{ProverError, VerifierError}; @@ -145,8 +143,7 @@ macro_rules! verify_ior { ) => {{ let __stmt = $statement; let __ins = $inputs; - $crate::protocol::ior::IOR::verify(&$ior, $transcript, &__stmt, &__ins).map( - |(reduced, outputs)| $crate::protocol::ior::IorVerifyResult { reduced, outputs }, - ) + $crate::protocol::ior::IOR::verify(&$ior, $transcript, &__stmt, &__ins) + .map(|(reduced, outputs)| $crate::protocol::ior::IorVerifyResult { reduced, outputs }) }}; } diff --git a/src/protocol/iors/batching.rs b/src/protocol/iors/batching.rs index 66e6a52..8643329 100644 --- a/src/protocol/iors/batching.rs +++ b/src/protocol/iors/batching.rs @@ -14,8 +14,8 @@ use std::marker::PhantomData; use crate::count_ops; use crate::error::VerifierError; -use crate::protocol::oracles::evaluation::Oracle; use crate::protocol::ior::{ProverTriple, IOR}; +use crate::protocol::oracles::evaluation::Oracle; use crate::protocol::transcript::EffscVerifierTranscript; use crate::utils::poly::{eq_poly, eq_poly_non_binary}; @@ -274,7 +274,9 @@ where .into_iter() .zip(&xi_eq_evals) .fold(F::zero(), |acc, (a, b)| acc + a * *b); - (expected == res.final_claim).then_some(()).ok_or(VerifierError::Target)?; + (expected == res.final_claim) + .then_some(()) + .ok_or(VerifierError::Target)?; Ok((BatchingReductionInputs { alpha: alpha_lsb }, ())) } diff --git a/src/protocol/iors/bridge.rs b/src/protocol/iors/bridge.rs index 9fbbc6d..4750377 100644 --- a/src/protocol/iors/bridge.rs +++ b/src/protocol/iors/bridge.rs @@ -9,9 +9,9 @@ use std::marker::PhantomData; use crate::count_ops; use crate::crypto::merkle::{warp_scheme, WarpCommitted}; use crate::error::{ProverError, VerifierError}; -use crate::protocol::oracles::evaluation::Oracle; -use crate::protocol::iors::twin_constraint::DeferredOracleCheck; use crate::protocol::ior::{ProverTriple, IOR}; +use crate::protocol::iors::twin_constraint::DeferredOracleCheck; +use crate::protocol::oracles::evaluation::Oracle; use crate::relations::BundledPESAT; pub struct BridgeStatement { diff --git a/src/protocol/iors/mod.rs b/src/protocol/iors/mod.rs index 45e191c..b43c422 100644 --- a/src/protocol/iors/mod.rs +++ b/src/protocol/iors/mod.rs @@ -1,9 +1,9 @@ //! Concrete Warp IORs, in protocol order. -pub mod pesat; -pub mod twin_constraint; +pub mod batching; pub mod bridge; pub mod ood; -pub mod sample_queries; -pub mod batching; +pub mod pesat; pub mod proximity; +pub mod sample_queries; +pub mod twin_constraint; diff --git a/src/protocol/iors/ood.rs b/src/protocol/iors/ood.rs index 0955c82..64f36c0 100644 --- a/src/protocol/iors/ood.rs +++ b/src/protocol/iors/ood.rs @@ -6,8 +6,8 @@ use std::marker::PhantomData; use crate::count_ops; use crate::error::VerifierError; -use crate::protocol::oracles::evaluation::Oracle; use crate::protocol::ior::{ProverTriple, IOR}; +use crate::protocol::oracles::evaluation::Oracle; pub struct OodStatement { pub s: usize, diff --git a/src/protocol/iors/proximity.rs b/src/protocol/iors/proximity.rs index e2cef18..45ec6b3 100644 --- a/src/protocol/iors/proximity.rs +++ b/src/protocol/iors/proximity.rs @@ -13,8 +13,8 @@ use std::marker::PhantomData; use crate::count_ops; use crate::crypto::merkle::{warp_scheme, WarpCommitted, WarpProof}; use crate::error::VerifierError; -use crate::protocol::oracles::indexed_merkle::IndexedOracle; use crate::protocol::ior::{ProverTriple, IOR}; +use crate::protocol::oracles::indexed_merkle::IndexedOracle; use crate::protocol::oracles::query_indices::QueryIndices; pub struct ProximityStatement { @@ -95,10 +95,10 @@ where Self: 'b; type VerifierInputs<'b> = ProximityVerifierInputs< - 'b, - F, - crate::protocol::oracles::indexed_merkle::MerkleIndexedOracle<'b, F, H>, - > + 'b, + F, + crate::protocol::oracles::indexed_merkle::MerkleIndexedOracle<'b, F, H>, + > where Self: 'b; type ReductionInputs = (); @@ -223,7 +223,9 @@ where H: 'c, { // Arity check: number of accumulator openings must match l2. - (inputs.acc.len() == statement.l2).then_some(()).ok_or(VerifierError::NumL2Instances)?; + (inputs.acc.len() == statement.l2) + .then_some(()) + .ok_or(VerifierError::NumL2Instances)?; // Validate each oracle handle. The handle is a partial function: // validate() runs the (lazy, memoized) BCS check internally — @@ -231,13 +233,17 @@ where inputs .fresh .validate() - .then_some(()).ok_or(VerifierError::ShiftQuery)?; + .then_some(()) + .ok_or(VerifierError::ShiftQuery)?; count_ops!( MerklePathsVerified, statement.queries.leaf_positions.len() as u64 ); for handle in inputs.acc.iter() { - handle.validate().then_some(()).ok_or(VerifierError::ShiftQuery)?; + handle + .validate() + .then_some(()) + .ok_or(VerifierError::ShiftQuery)?; count_ops!( MerklePathsVerified, statement.queries.leaf_positions.len() as u64 diff --git a/src/protocol/iors/twin_constraint.rs b/src/protocol/iors/twin_constraint.rs index 815d3f6..cabc168 100644 --- a/src/protocol/iors/twin_constraint.rs +++ b/src/protocol/iors/twin_constraint.rs @@ -19,16 +19,16 @@ use std::marker::PhantomData; use crate::count_ops; use crate::error::VerifierError; -use crate::protocol::oracles::evaluation::Oracle; use crate::protocol::ior::{ProverTriple, IOR}; +use crate::protocol::oracles::evaluation::Oracle; use crate::protocol::transcript::EffscVerifierTranscript; use crate::relations::r1cs::R1CSConstraints; -use crate::warp::AccumulatorInstance; use crate::utils::{ concat_slices, poly::{eq_poly, eq_poly_non_binary}, scale_and_sum, }; +use crate::warp::AccumulatorInstance; /// Degree-1 polynomial interpolating two field elements: `lo + (hi - lo)·X`. fn linear_poly(lo: F, hi: F) -> DensePolynomial { @@ -184,7 +184,9 @@ pub struct DeferredOracleCheck { impl DeferredOracleCheck { pub fn discharge(&self, gamma: &[F], nu_0: F, eta: F) -> Result<(), VerifierError> { let expected = eq_poly_non_binary(&self.tau, gamma) * (nu_0 + self.omega * eta); - (expected == self.claim).then_some(()).ok_or(VerifierError::Target) + (expected == self.claim) + .then_some(()) + .ok_or(VerifierError::Target) } } diff --git a/src/warp/prove.rs b/src/warp/prove.rs index 3b05cae..b08bfbc 100644 --- a/src/warp/prove.rs +++ b/src/warp/prove.rs @@ -6,7 +6,6 @@ use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState use std::marker::PhantomData; use crate::error::ProverError; -use crate::prove_ior; use crate::protocol::ior::IorProveResult; use crate::protocol::iors::{ batching::{ @@ -27,6 +26,7 @@ use crate::protocol::iors::{ }, }; use crate::protocol::transcript::absorb_instances; +use crate::prove_ior; use crate::relations::{r1cs::R1CSConstraints, BundledPESAT}; use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; use crate::warp::keys::WARPProverKey; @@ -129,12 +129,13 @@ where )?; let IorProveResult { - reduced: TwinConstraintReducedStatement { - gamma: _, - zeta_0, - beta_tau, - deferred: _, - }, + reduced: + TwinConstraintReducedStatement { + gamma: _, + zeta_0, + beta_tau, + deferred: _, + }, proof: _, witness: TwinConstraintReducedWitness { f, z }, } = prove_ior!( @@ -160,13 +161,19 @@ where )?; let IorProveResult { - reduced: BridgeReducedStatement { - eta, - nu_0, - td_new_root: _, - }, + reduced: + BridgeReducedStatement { + eta, + nu_0, + td_new_root: _, + }, proof: _, - witness: BridgeReducedWitness { td_new, new_x, new_w }, + witness: + BridgeReducedWitness { + td_new, + new_x, + new_w, + }, } = prove_ior!( bridge_ior, prover_state, @@ -186,7 +193,11 @@ where )?; let IorProveResult { - reduced: OodReducedStatement { samples_flat, answers }, + reduced: + OodReducedStatement { + samples_flat, + answers, + }, proof: _, witness: _, } = prove_ior!( @@ -230,11 +241,12 @@ where let IorProveResult { reduced: _, - proof: ProximityProofString { - auth_0, - auth_j, - shift_query_answers, - }, + proof: + ProximityProofString { + auth_0, + auth_j, + shift_query_answers, + }, witness: _, } = prove_ior!( proximity_ior, diff --git a/src/warp/verify.rs b/src/warp/verify.rs index ff7f9cc..733354d 100644 --- a/src/warp/verify.rs +++ b/src/warp/verify.rs @@ -10,9 +10,7 @@ use crate::crypto::merkle::warp_scheme; use crate::error::VerifierError; use crate::protocol::ior::IorVerifyResult; use crate::protocol::iors::{ - batching::{ - Batching, BatchingReducedStatement, BatchingStatement, BatchingVerifierInputs, - }, + batching::{Batching, BatchingReducedStatement, BatchingStatement, BatchingVerifierInputs}, bridge::{Bridge, BridgeReducedStatement, BridgeStatement, BridgeVerifierInputs}, ood::{Ood, OodReducedStatement, OodStatement}, pesat::{Pesat, PesatReducedStatement, PesatStatement, PesatVerifierOutputs}, @@ -84,10 +82,11 @@ where }; let IorVerifyResult { - reduced: PesatReducedStatement { - mus: l1_mus, - taus: l1_taus, - }, + reduced: + PesatReducedStatement { + mus: l1_mus, + taus: l1_taus, + }, outputs: PesatVerifierOutputs { rt_0 }, } = verify_ior!( pesat_ior, @@ -97,12 +96,13 @@ where )?; let IorVerifyResult { - reduced: TwinConstraintReducedStatement { - gamma, - zeta_0, - beta_tau, - deferred, - }, + reduced: + TwinConstraintReducedStatement { + gamma, + zeta_0, + beta_tau, + deferred, + }, outputs: _, } = verify_ior!( twin_constraint_ior, @@ -119,11 +119,12 @@ where )?; let IorVerifyResult { - reduced: BridgeReducedStatement { - eta: _, - nu_0, - td_new_root: _, - }, + reduced: + BridgeReducedStatement { + eta: _, + nu_0, + td_new_root: _, + }, outputs: _, } = verify_ior!( bridge_ior, @@ -141,7 +142,11 @@ where )?; let IorVerifyResult { - reduced: OodReducedStatement { samples_flat, answers }, + reduced: + OodReducedStatement { + samples_flat, + answers, + }, outputs: _, } = verify_ior!( ood_ior, diff --git a/tests/integration_warp.rs b/tests/integration_warp.rs index e55917b..42465b6 100644 --- a/tests/integration_warp.rs +++ b/tests/integration_warp.rs @@ -32,8 +32,8 @@ use warp::relations::{ BundledPESAT, Relation, ToPolySystem, }; use warp::serialize::acc_witness_size; -use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProverKey, WARPVerifierKey}; use warp::utils::poseidon; +use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProverKey, WARPVerifierKey}; use warp::WARP; #[test] @@ -94,7 +94,12 @@ fn warp_test() { let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((new_x, new_w), _pf) = hash_chain_warp .prove( - WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, + WARPProverKey { + index: r1cs.clone(), + m: r1cs.m, + n: r1cs.n, + k: r1cs.k, + }, &mut prover_state, instances_witnesses.1.clone(), instances_witnesses.0.clone(), @@ -120,7 +125,12 @@ fn warp_test() { let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), pf) = hash_chain_warp .prove( - WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, + WARPProverKey { + index: r1cs.clone(), + m: r1cs.m, + n: r1cs.n, + k: r1cs.k, + }, &mut prover_state, instances_witnesses.1, instances_witnesses.0, @@ -137,7 +147,11 @@ fn warp_test() { .std_verifier(&narg_str); hash_chain_warp .verify( - WARPVerifierKey { m: r1cs.m, n: r1cs.n, k: r1cs.k }, + WARPVerifierKey { + m: r1cs.m, + n: r1cs.n, + k: r1cs.k, + }, &mut verifier_state, acc_x.clone(), pf.clone(), @@ -214,7 +228,12 @@ fn warp_test_goldilocks() { let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((new_x, new_w), _pf) = hash_chain_warp .prove( - WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, + WARPProverKey { + index: r1cs.clone(), + m: r1cs.m, + n: r1cs.n, + k: r1cs.k, + }, &mut prover_state, instances_witnesses.1.clone(), instances_witnesses.0.clone(), @@ -242,7 +261,12 @@ fn warp_test_goldilocks() { let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), pf) = hash_chain_warp .prove( - WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, + WARPProverKey { + index: r1cs.clone(), + m: r1cs.m, + n: r1cs.n, + k: r1cs.k, + }, &mut prover_state, instances_witnesses.1, instances_witnesses.0, @@ -259,7 +283,11 @@ fn warp_test_goldilocks() { .std_verifier(&narg_str); hash_chain_warp .verify( - WARPVerifierKey { m: r1cs.m, n: r1cs.n, k: r1cs.k }, + WARPVerifierKey { + m: r1cs.m, + n: r1cs.n, + k: r1cs.k, + }, &mut verifier_state, acc_x.clone(), pf.clone(), @@ -277,6 +305,9 @@ fn warp_test_goldilocks() { "Goldilocks acc_w size: {}", acc_witness_size(&acc_w, Compress::Yes) ); - println!("Goldilocks proof size: {}", pf.serialized_size(Compress::Yes)); + println!( + "Goldilocks proof size: {}", + pf.serialized_size(Compress::Yes) + ); println!("Goldilocks narg_str size: {}", narg_str.len()); } diff --git a/tests/profile_json.rs b/tests/profile_json.rs index ac35b01..c261917 100644 --- a/tests/profile_json.rs +++ b/tests/profile_json.rs @@ -27,8 +27,8 @@ use warp::relations::{ }, BundledPESAT, Relation, ToPolySystem, }; -use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProverKey}; use warp::utils::poseidon; +use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProverKey}; use warp::WARP; /// `Arc>>` wrapped so it implements `io::Write`. @@ -102,7 +102,12 @@ fn json_layer_emits_phase_records() { hash_chain_warp .prove( - WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, + WARPProverKey { + index: r1cs.clone(), + m: r1cs.m, + n: r1cs.n, + k: r1cs.k, + }, &mut prover_state, witnesses, instances, diff --git a/tests/verifier_negative.rs b/tests/verifier_negative.rs index abe850a..ce7f8fc 100644 --- a/tests/verifier_negative.rs +++ b/tests/verifier_negative.rs @@ -39,8 +39,10 @@ use warp::relations::{ }, BundledPESAT, Relation, ToPolySystem, }; -use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProof, WARPProverKey, WARPVerifierKey}; use warp::utils::poseidon; +use warp::warp::{ + AccumulatorInstance, AccumulatorWitness, WARPProof, WARPProverKey, WARPVerifierKey, +}; use warp::WARP; type F = BLS12_381; @@ -127,7 +129,12 @@ fn make_fixture() -> Fixture { let mut ps = ds.without_session().instance(&0u32).std_prover(); let ((new_x, new_w), _) = w1 .prove( - WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, + WARPProverKey { + index: r1cs.clone(), + m: r1cs.m, + n: r1cs.n, + k: r1cs.k, + }, &mut ps, witnesses.clone(), instances.clone(), @@ -148,7 +155,12 @@ fn make_fixture() -> Fixture { let mut ps = ds.without_session().instance(&0u32).std_prover(); let ((acc_x, _acc_w), proof) = warp .prove( - WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, + WARPProverKey { + index: r1cs.clone(), + m: r1cs.m, + n: r1cs.n, + k: r1cs.k, + }, &mut ps, witnesses, instances, @@ -159,7 +171,11 @@ fn make_fixture() -> Fixture { Fixture { warp, - vk: WARPVerifierKey { m: r1cs.m, n: r1cs.n, k: r1cs.k }, + vk: WARPVerifierKey { + m: r1cs.m, + n: r1cs.n, + k: r1cs.k, + }, acc_x, proof, narg_str: ps.narg_string().to_vec(), @@ -288,12 +304,8 @@ fn prove_rejects_mismatched_instance_witness_lengths() { .unzip(); let warp_cfg = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); - let warp = WARP::, _, H>::new( - warp_cfg, - code, - r1cs.clone(), - Blake3FieldHasher::::new(), - ); + let warp = + WARP::, _, H>::new(warp_cfg, code, r1cs.clone(), Blake3FieldHasher::::new()); let mut witnesses_short = witnesses; witnesses_short.pop(); @@ -301,7 +313,12 @@ fn prove_rejects_mismatched_instance_witness_lengths() { let ds = spongefish::domain_separator!("test::warp::prove_negative"); let mut ps = ds.without_session().instance(&0u32).std_prover(); let result = warp.prove( - WARPProverKey { index: r1cs.clone(), m: r1cs.m, n: r1cs.n, k: r1cs.k }, + WARPProverKey { + index: r1cs.clone(), + m: r1cs.m, + n: r1cs.n, + k: r1cs.k, + }, &mut ps, witnesses_short, instances, From bd8886e67a08f717baa730339e8266f342f4d3dc Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 12 May 2026 18:14:55 +0200 Subject: [PATCH 62/68] more cleanup --- benches/utils/hash_chain.rs | 4 +- benches/warp_rs.rs | 15 +- src/config/mod.rs | 44 +++--- src/error.rs | 2 +- src/protocol/iors/batching.rs | 72 ++++++---- src/protocol/iors/bridge.rs | 48 ++++--- src/protocol/iors/ood.rs | 15 +- src/protocol/iors/pesat.rs | 46 +++--- src/protocol/iors/proximity.rs | 32 ++--- src/protocol/iors/sample_queries.rs | 12 +- src/protocol/iors/twin_constraint.rs | 74 +++++----- src/protocol/transcript/prover.rs | 12 +- src/protocol/transcript/verifier.rs | 10 +- src/relations/mod.rs | 15 +- src/relations/r1cs/hashchain/mod.rs | 9 +- src/relations/r1cs/hashchain/relation.rs | 6 +- src/relations/r1cs/mod.rs | 66 +++++---- src/serialize.rs | 54 ++++--- src/warp/accumulator.rs | 49 +++---- src/warp/decide.rs | 24 ++-- src/warp/keys.rs | 12 +- src/warp/params.rs | 8 +- src/warp/proof.rs | 16 +-- src/warp/prove.rs | 174 ++++++++++++++--------- src/warp/scheme.rs | 27 ++-- src/warp/verify.rs | 147 ++++++++++--------- tests/integration_warp.rs | 64 +++++---- tests/profile_json.rs | 17 ++- tests/verifier_negative.rs | 54 +++---- 29 files changed, 638 insertions(+), 490 deletions(-) diff --git a/benches/utils/hash_chain.rs b/benches/utils/hash_chain.rs index 7cfd6ef..ffb2cde 100644 --- a/benches/utils/hash_chain.rs +++ b/benches/utils/hash_chain.rs @@ -9,7 +9,7 @@ use warp::relations::{ hashchain::{compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness}, R1CS, }, - Relation, ToPolySystem, + Arithmetize, Relation, }; // utilities for the hashchain benchmark @@ -17,7 +17,7 @@ pub fn get_hashchain_r1cs( poseidon_config: &PoseidonConfig, hashchain_size: usize, ) -> R1CS { - HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + HashChainRelation::, CRHGadget<_>>::arithmetize(&( poseidon_config.clone(), hashchain_size, )) diff --git a/benches/warp_rs.rs b/benches/warp_rs.rs index 08f65c1..3cce80d 100644 --- a/benches/warp_rs.rs +++ b/benches/warp_rs.rs @@ -13,7 +13,7 @@ use warp::WARP; mod utils; use utils::poseidon; -use warp::relations::BundledPESAT; +use warp::relations::PolyPredicate; use warp::utils::fields::Goldilocks; const HASHCHAIN_SIZE: usize = 800; @@ -24,13 +24,16 @@ pub fn bench_rs_warp_fields(c: &mut Criterion) { let poseidon_config = poseidon::initialize_poseidon_config::(); let r1cs = get_hashchain_r1cs(&poseidon_config, HASHCHAIN_SIZE); - let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code_config = ReedSolomonConfig::::default( + r1cs.k_num_witness_vars, + r1cs.k_num_witness_vars.next_power_of_two(), + ); let code = ReedSolomon::new(code_config.clone()); let s = 2; let t = 125; for l in [32, 64, 128, 256, 512] { - let warp_config = WARPConfig::new(l, l, s, t, r1cs.config(), code.code_len()); + let warp_config = WARPConfig::new(l, 0, s, t, r1cs.config(), code.code_len()); let hash_chain_warp = WARP::<_, _, _, Blake3FieldHasher>::new( warp_config.clone(), @@ -58,9 +61,9 @@ pub fn bench_rs_warp_fields(c: &mut Criterion) { .prove( WARPProverKey { index: r1cs.clone(), - m: r1cs.m, - n: r1cs.n, - k: r1cs.k, + m_num_constraints: r1cs.m_num_constraints, + n_num_variables: r1cs.n_num_variables, + k_num_witness_vars: r1cs.k_num_witness_vars, }, &mut prover_state, instances_witnesses.1.clone(), diff --git a/src/config/mod.rs b/src/config/mod.rs index 45be824..555b198 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,26 +1,38 @@ use ark_ff::Field; -use crate::relations::BundledPESAT; +use crate::relations::PolyPredicate; #[derive(Clone)] -pub struct WARPConfig> { - pub l: usize, - pub l1: usize, - pub s: usize, - pub t: usize, - pub p_conf: P::Config, - pub n: usize, +pub struct WARPConfig> { + pub l1_first_fold_factor: usize, + pub l2_second_fold_factor: usize, + pub s_num_ood_samples: usize, + pub t_num_queries: usize, + pub predicate_config: P::Config, + pub n_code_len: usize, } -impl> WARPConfig { - pub fn new(l: usize, l1: usize, s: usize, t: usize, p_conf: P::Config, n: usize) -> Self { +impl> WARPConfig { + pub fn new( + l1_first_fold_factor: usize, + l2_second_fold_factor: usize, + s_num_ood_samples: usize, + t_num_queries: usize, + predicate_config: P::Config, + n_code_len: usize, + ) -> Self { Self { - l, - l1, - s, - t, - p_conf, - n, + l1_first_fold_factor, + l2_second_fold_factor, + s_num_ood_samples, + t_num_queries, + predicate_config, + n_code_len, } } + + /// Total fold factor `l = l1 + l2`. + pub fn l_total_fold_factor(&self) -> usize { + self.l1_first_fold_factor + self.l2_second_fold_factor + } } diff --git a/src/error.rs b/src/error.rs index f451298..f504b67 100644 --- a/src/error.rs +++ b/src/error.rs @@ -29,7 +29,7 @@ pub enum ProverError { InsufficientInstances { got: usize }, #[error("instances.len() ({instances}) != witnesses.len() ({witnesses})")] InstanceWitnessLengthMismatch { instances: usize, witnesses: usize }, - #[error("acc_witness.td.len() ({roots}) != acc_instance.rt.len() ({instances})")] + #[error("acc_witness.td_committed_codewords.len() ({roots}) != acc_instance.rt_merkle_roots.len() ({instances})")] AccumulatorShapeMismatch { instances: usize, roots: usize }, #[error("config parameter invalid: {reason}")] ConfigParameterInvalid { reason: String }, diff --git a/src/protocol/iors/batching.rs b/src/protocol/iors/batching.rs index 8643329..edfff05 100644 --- a/src/protocol/iors/batching.rs +++ b/src/protocol/iors/batching.rs @@ -22,19 +22,19 @@ use crate::utils::poly::{eq_poly, eq_poly_non_binary}; /// Sparse-eval optimization (CBBZ23 / HyperPlonk): shift-query zetas at /// indices `1+s..r` are 0/1 vectors picking a single hypercube point. fn accumulate_sparse_evaluations( - zetas: &[Vec], - eq_evals: &[F], - s: usize, - r: usize, + zetas_evaluation_points: &[Vec], + xi_eq_evals: &[F], + s_num_ood_samples: usize, + r_total_points: usize, ) -> HashMap { let mut result: HashMap = HashMap::new(); - for i in 1 + s..r { - let index = zetas[i] + for i in 1 + s_num_ood_samples..r_total_points { + let index = zetas_evaluation_points[i] .iter() .enumerate() .filter_map(|(j, bit)| bit.is_one().then_some(1 << j)) .sum::(); - *result.entry(index).or_insert_with(F::zero) += eq_evals[i]; + *result.entry(index).or_insert_with(F::zero) += xi_eq_evals[i]; } result } @@ -61,8 +61,8 @@ fn batched_constraint_poly( pub struct BatchingStatement { /// `1 + s + t` evaluation points: `[ζ_0, ood_chunk_0…, query_k…]`. pub zetas_prefix: Vec>, - pub s: usize, - pub t: usize, + pub s_num_ood_samples: usize, + pub t_num_queries: usize, pub log_n: usize, pub _phantom: std::marker::PhantomData, } @@ -74,11 +74,11 @@ impl BatchingStatement { zeta_0: Vec, ood_samples_flat: &[F], query_eval_points: &[Vec], - s: usize, - t: usize, + s_num_ood_samples: usize, + t_num_queries: usize, log_n: usize, ) -> Self { - let mut zetas: Vec> = Vec::with_capacity(1 + s + t); + let mut zetas: Vec> = Vec::with_capacity(1 + s_num_ood_samples + t_num_queries); zetas.push(zeta_0); for chunk in ood_samples_flat.chunks(log_n) { zetas.push(chunk.to_vec()); @@ -88,8 +88,8 @@ impl BatchingStatement { } Self { zetas_prefix: zetas, - s, - t, + s_num_ood_samples, + t_num_queries, log_n, _phantom: std::marker::PhantomData, } @@ -101,20 +101,20 @@ pub struct BatchingProverInputs<'a, F: Field> { } pub struct BatchingVerifierInputs { - pub nus: Vec, + pub nus_claimed_evals: Vec, pub acc_mu: F, } pub struct BatchingReductionInputs { - pub alpha: Vec, + pub alpha_sumcheck_challenges: Vec, } pub struct BatchingReducedStatement { - pub alpha: Vec, + pub alpha_sumcheck_challenges: Vec, } pub struct BatchingReducedWitness { - pub mu: F, + pub mu_claimed_eval: F, } #[derive(Default)] @@ -157,14 +157,14 @@ where Self: 'b, { BatchingReducedStatement { - alpha: inputs.alpha.clone(), + alpha_sumcheck_challenges: inputs.alpha_sumcheck_challenges.clone(), } } #[tracing::instrument( name = "batching", skip_all, - fields(s = statement.s, t = statement.t, log_n = statement.log_n) + fields(s = statement.s_num_ood_samples, t = statement.t_num_queries, log_n = statement.log_n) )] fn prove_inner<'b>( &self, @@ -177,7 +177,7 @@ where Self: 'b, { let n = inputs.oracle.len(); - let r = 1 + statement.s + statement.t; + let r = 1 + statement.s_num_ood_samples + statement.t_num_queries; let log_r = log2(r) as usize; debug_assert_eq!(statement.zetas_prefix.len(), r); @@ -186,7 +186,7 @@ where let (xi_eq_evals, ood_evals_vec) = { let _s = tracing::info_span!("batching.eq_evals").entered(); let xi_eq_evals = (0..r).map(|i| eq_poly(&xis, i)).collect::>(); - let ood_evals_vec = (0..1 + statement.s) + let ood_evals_vec = (0..1 + statement.s_num_ood_samples) .map(|i| { (0..n) .map(|a| eq_poly(&statement.zetas_prefix[i], a) * xi_eq_evals[i]) @@ -198,7 +198,12 @@ where let id_non_0_eval_sums = { let _s = tracing::info_span!("batching.accumulate_sparse").entered(); - accumulate_sparse_evaluations(&statement.zetas_prefix, &xi_eq_evals, statement.s, r) + accumulate_sparse_evaluations( + &statement.zetas_prefix, + &xi_eq_evals, + statement.s_num_ood_samples, + r, + ) }; // Run the inner-product sumcheck. MSB half-split → reverse once. @@ -220,17 +225,19 @@ where Ok(( BatchingReductionInputs { - alpha: alpha.clone(), + alpha_sumcheck_challenges: alpha.clone(), }, (), - BatchingReducedWitness { mu }, + BatchingReducedWitness { + mu_claimed_eval: mu, + }, )) } #[tracing::instrument( name = "batching.verify", skip_all, - fields(s = statement.s, t = statement.t, log_n = statement.log_n) + fields(s = statement.s_num_ood_samples, t = statement.t_num_queries, log_n = statement.log_n) )] fn verify_inner<'b, 'c>( &self, @@ -241,10 +248,10 @@ where where Self: 'c, { - let r = 1 + statement.s + statement.t; + let r = 1 + statement.s_num_ood_samples + statement.t_num_queries; let log_r = log2(r) as usize; debug_assert_eq!(statement.zetas_prefix.len(), r); - debug_assert_eq!(inputs.nus.len(), r); + debug_assert_eq!(inputs.nus_claimed_evals.len(), r); // Squeeze ξ matching the prover. let xis: Vec = (0..log_r) @@ -255,7 +262,7 @@ where // σ₂ = Σ ξ_eq · ν. let sigma_2 = xi_eq_evals .iter() - .zip(&inputs.nus) + .zip(&inputs.nus_claimed_evals) .fold(F::zero(), |acc, (xi_eq, nu)| acc + *xi_eq * nu); // Run sumcheck_verify and check the final-claim oracle check. @@ -278,6 +285,11 @@ where .then_some(()) .ok_or(VerifierError::Target)?; - Ok((BatchingReductionInputs { alpha: alpha_lsb }, ())) + Ok(( + BatchingReductionInputs { + alpha_sumcheck_challenges: alpha_lsb, + }, + (), + )) } } diff --git a/src/protocol/iors/bridge.rs b/src/protocol/iors/bridge.rs index 4750377..707b6e9 100644 --- a/src/protocol/iors/bridge.rs +++ b/src/protocol/iors/bridge.rs @@ -12,7 +12,7 @@ use crate::error::{ProverError, VerifierError}; use crate::protocol::ior::{ProverTriple, IOR}; use crate::protocol::iors::twin_constraint::DeferredOracleCheck; use crate::protocol::oracles::evaluation::Oracle; -use crate::relations::BundledPESAT; +use crate::relations::PolyPredicate; pub struct BridgeStatement { pub zeta_0: Vec, @@ -22,17 +22,17 @@ pub struct BridgeStatement { } pub struct BridgeWitness<'a, F: Field> { - pub z: &'a [F], - pub f: &'a Oracle, + pub z_witness_assignment: &'a [F], + pub f_oracle: &'a Oracle, } pub struct BridgeProverInputs<'a, F, P, H> where F: Field, - P: BundledPESAT, + P: PolyPredicate, H: MerkleHasher>, { - pub bundled_pesat: &'a P, + pub predicate: &'a P, pub hasher: &'a H, pub code_len: usize, pub _f: PhantomData, @@ -40,18 +40,18 @@ where pub struct BridgeVerifierInputs<'a, F: Field> { pub deferred: &'a DeferredOracleCheck, - pub gamma: &'a [F], + pub gamma_twin_constraint_challenges: &'a [F], } pub struct BridgeReductionInputs { - pub eta: F, - pub nu_0: F, + pub eta_predicate_eval: F, + pub nu_0_oracle_eval: F, pub td_new_root: H::Digest, } pub struct BridgeReducedStatement { - pub eta: F, - pub nu_0: F, + pub eta_predicate_eval: F, + pub nu_0_oracle_eval: F, pub td_new_root: H::Digest, } @@ -76,7 +76,7 @@ impl Default for Bridge { impl IOR for Bridge where F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - P: BundledPESAT, + P: PolyPredicate, H: MerkleHasher>, H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize + Clone, { @@ -113,8 +113,8 @@ where Self: 'a, { BridgeReducedStatement { - eta: inputs.eta, - nu_0: inputs.nu_0, + eta_predicate_eval: inputs.eta_predicate_eval, + nu_0_oracle_eval: inputs.nu_0_oracle_eval, td_new_root: inputs.td_new_root.clone(), } } @@ -137,15 +137,15 @@ where // η = ⟨ eq(β_τ), p(z) ⟩ let beta_eq_evals = compute_hypercube_eq_evals(statement.log_m, &statement.beta_tau); let eta = inputs - .bundled_pesat - .evaluate_bundled(&beta_eq_evals, witness.z) + .predicate + .evaluate_bundled(&beta_eq_evals, witness.z_witness_assignment) .map_err(|_| ProverError::SpongeFish)?; // ν₀ = f̂(ζ₀) - let nu_0 = witness.f.query_at_point(&statement.zeta_0); + let nu_0 = witness.f_oracle.query_at_point(&statement.zeta_0); // (new_x, new_w) = z[..N-k], z[N-k..] - let (new_x_slice, new_w_slice) = witness.z.split_at(statement.n_minus_k); + let (new_x_slice, new_w_slice) = witness.z_witness_assignment.split_at(statement.n_minus_k); let new_x = new_x_slice.to_vec(); let new_w = new_w_slice.to_vec(); @@ -154,7 +154,7 @@ where let _s = tracing::info_span!("bridge.commit_new_oracle").entered(); count_ops!(MerkleTreeBuilds); let scheme = warp_scheme::(inputs.hasher.clone(), inputs.code_len); - scheme.commit(&[witness.f.evals().to_vec()]) + scheme.commit(&[witness.f_oracle.evals().to_vec()]) }; let td_new_root = td_new.root().clone(); @@ -165,8 +165,8 @@ where Ok(( BridgeReductionInputs { - eta, - nu_0, + eta_predicate_eval: eta, + nu_0_oracle_eval: nu_0, td_new_root, }, (), @@ -194,12 +194,14 @@ where let nu_0: F = verifier_state.prover_message()?; // Discharge TC's deferred check. - inputs.deferred.discharge(inputs.gamma, nu_0, eta)?; + inputs + .deferred + .discharge(inputs.gamma_twin_constraint_challenges, nu_0, eta)?; Ok(( BridgeReductionInputs { - eta, - nu_0, + eta_predicate_eval: eta, + nu_0_oracle_eval: nu_0, td_new_root, }, (), diff --git a/src/protocol/iors/ood.rs b/src/protocol/iors/ood.rs index 64f36c0..7b41383 100644 --- a/src/protocol/iors/ood.rs +++ b/src/protocol/iors/ood.rs @@ -10,7 +10,7 @@ use crate::protocol::ior::{ProverTriple, IOR}; use crate::protocol::oracles::evaluation::Oracle; pub struct OodStatement { - pub s: usize, + pub s_num_ood_samples: usize, pub log_n: usize, } @@ -73,7 +73,7 @@ where } } - #[tracing::instrument(name = "ood", skip_all, fields(s = statement.s, log_n = statement.log_n))] + #[tracing::instrument(name = "ood", skip_all, fields(s = statement.s_num_ood_samples, log_n = statement.log_n))] fn prove_inner<'b>( &self, prover_state: &mut ProverState, @@ -84,8 +84,9 @@ where where Self: 'b, { - let samples_flat = prover_state.verifier_messages_vec::(statement.s * statement.log_n); - count_ops!(OodPointQueries, statement.s as u64); + let samples_flat = + prover_state.verifier_messages_vec::(statement.s_num_ood_samples * statement.log_n); + count_ops!(OodPointQueries, statement.s_num_ood_samples as u64); let answers = samples_flat .chunks(statement.log_n) .map(|zeta| inputs.oracle.query_at_point(zeta)) @@ -104,7 +105,7 @@ where #[tracing::instrument( name = "ood.verify", skip_all, - fields(s = statement.s, log_n = statement.log_n) + fields(s = statement.s_num_ood_samples, log_n = statement.log_n) )] fn verify_inner<'b, 'c>( &self, @@ -115,10 +116,10 @@ where where Self: 'c, { - let samples_flat: Vec = (0..statement.s * statement.log_n) + let samples_flat: Vec = (0..statement.s_num_ood_samples * statement.log_n) .map(|_| verifier_state.verifier_message::()) .collect(); - let answers: Vec = verifier_state.prover_messages_vec(statement.s)?; + let answers: Vec = verifier_state.prover_messages_vec(statement.s_num_ood_samples)?; Ok(( OodReductionInputs { samples_flat, diff --git a/src/protocol/iors/pesat.rs b/src/protocol/iors/pesat.rs index e59b736..0afa3e5 100644 --- a/src/protocol/iors/pesat.rs +++ b/src/protocol/iors/pesat.rs @@ -16,7 +16,7 @@ use crate::error::VerifierError; use crate::protocol::ior::{ProverTriple, IOR}; pub struct PesatStatement { - pub l1: usize, + pub l1_first_fold_factor: usize, pub log_m: usize, } @@ -25,13 +25,13 @@ pub struct PesatWitness<'a, F: Field> { } pub struct PesatReductionInputs { - pub mus: Vec, - pub taus: Vec>, + pub mus_codeword_first_coords: Vec, + pub taus_zero_check_challenges: Vec>, } pub struct PesatReducedStatement { - pub mus: Vec, - pub taus: Vec>, + pub mus_codeword_first_coords: Vec, + pub taus_zero_check_challenges: Vec>, } pub struct PesatReducedWitness @@ -40,11 +40,11 @@ where H: MerkleHasher>, { pub codewords: Vec>, - pub td_0: WarpCommitted, + pub td_0_committed_codeword: WarpCommitted, } pub struct PesatVerifierOutputs { - pub rt_0: H::Digest, + pub rt_0_fresh_merkle_root: H::Digest, } /// PESAT IOR configuration. @@ -98,15 +98,15 @@ where Self: 'b, { PesatReducedStatement { - mus: inputs.mus.clone(), - taus: inputs.taus.clone(), + mus_codeword_first_coords: inputs.mus_codeword_first_coords.clone(), + taus_zero_check_challenges: inputs.taus_zero_check_challenges.clone(), } } #[tracing::instrument( name = "pesat", skip_all, - fields(l1 = statement.l1, log_m = statement.log_m, n_witnesses = witness.witnesses.len()) + fields(l1 = statement.l1_first_fold_factor, log_m = statement.log_m, n_witnesses = witness.witnesses.len()) )] fn prove_inner<'b>( &self, @@ -139,25 +139,28 @@ where prover_state.prover_message(td_0.root()); prover_state.prover_messages(&mus); - (0..statement.l1) + (0..statement.l1_first_fold_factor) .map(|_| prover_state.verifier_messages_vec::(statement.log_m)) .collect::>() }; Ok(( PesatReductionInputs { - mus: mus.clone(), - taus, + mus_codeword_first_coords: mus.clone(), + taus_zero_check_challenges: taus, }, (), - PesatReducedWitness { codewords, td_0 }, + PesatReducedWitness { + codewords, + td_0_committed_codeword: td_0, + }, )) } #[tracing::instrument( name = "pesat.verify", skip_all, - fields(l1 = statement.l1, log_m = statement.log_m) + fields(l1 = statement.l1_first_fold_factor, log_m = statement.log_m) )] fn verify_inner<'b, 'c>( &self, @@ -170,8 +173,8 @@ where H: 'c, { let rt_0: H::Digest = verifier_state.prover_message()?; - let mus: Vec = verifier_state.prover_messages_vec(statement.l1)?; - let taus: Vec> = (0..statement.l1) + let mus: Vec = verifier_state.prover_messages_vec(statement.l1_first_fold_factor)?; + let taus: Vec> = (0..statement.l1_first_fold_factor) .map(|_| { (0..statement.log_m) .map(|_| verifier_state.verifier_message::()) @@ -180,8 +183,13 @@ where .collect(); Ok(( - PesatReductionInputs { mus, taus }, - PesatVerifierOutputs { rt_0 }, + PesatReductionInputs { + mus_codeword_first_coords: mus, + taus_zero_check_challenges: taus, + }, + PesatVerifierOutputs { + rt_0_fresh_merkle_root: rt_0, + }, )) } } diff --git a/src/protocol/iors/proximity.rs b/src/protocol/iors/proximity.rs index 45ec6b3..c4eb7c6 100644 --- a/src/protocol/iors/proximity.rs +++ b/src/protocol/iors/proximity.rs @@ -19,11 +19,11 @@ use crate::protocol::oracles::query_indices::QueryIndices; pub struct ProximityStatement { pub queries: QueryIndices, - pub l2: usize, - pub t: usize, + pub l2_second_fold_factor: usize, + pub t_num_queries: usize, /// Codeword length (`code.code_len()`), needed by both prover and /// verifier to construct the `WarpScheme` used for open/check. - pub n: usize, + pub n_code_len: usize, } pub struct ProximityProverInputs<'a, F, H> @@ -31,8 +31,8 @@ where F: Field, H: MerkleHasher>, { - pub td_0: &'a WarpCommitted, - pub acc_td: &'a [WarpCommitted], + pub td_0_committed_codeword: &'a WarpCommitted, + pub acc_td_committed_codewords: &'a [WarpCommitted], } /// Verifier-side inputs. The IOR sees [`IndexedOracle`] handles, not @@ -122,7 +122,7 @@ where skip_all, fields( n_queries = statement.queries.leaf_positions.len(), - n_accumulators = inputs.acc_td.len(), + n_accumulators = inputs.acc_td_committed_codewords.len(), ) )] fn prove_inner<'b>( @@ -147,22 +147,22 @@ where sorted_unique.sort_unstable(); sorted_unique.dedup(); - let scheme = warp_scheme(self.hasher.clone(), statement.n); + let scheme = warp_scheme(self.hasher.clone(), statement.n_code_len); let auth_0 = { let _s = tracing::info_span!("proximity.auth_0").entered(); count_ops!(MerklePathsGenerated, sorted_unique.len() as u64); - scheme.open(inputs.td_0, &sorted_unique) + scheme.open(inputs.td_0_committed_codeword, &sorted_unique) }; let auth_j: Vec> = { let _s = tracing::info_span!("proximity.auth_j").entered(); count_ops!( MerklePathsGenerated, - (inputs.acc_td.len() * sorted_unique.len()) as u64 + (inputs.acc_td_committed_codewords.len() * sorted_unique.len()) as u64 ); inputs - .acc_td + .acc_td_committed_codewords .iter() .map(|td| scheme.open(td, &sorted_unique)) .collect() @@ -173,21 +173,21 @@ where let shift_query_answers = { let _s = tracing::info_span!("proximity.shift_queries").entered(); let total_codewords = inputs - .acc_td + .acc_td_committed_codewords .iter() .map(|td| td.num_codewords()) .sum::() - + inputs.td_0.num_codewords(); + + inputs.td_0_committed_codeword.num_codewords(); let mut answers = vec![vec![F::default(); total_codewords]; leaf_positions.len()]; for (qi, idx) in leaf_positions.iter().enumerate() { let mut col = 0usize; - for td in inputs.acc_td.iter() { + for td in inputs.acc_td_committed_codewords.iter() { for cw in td.codewords() { answers[qi][col] = cw[*idx]; col += 1; } } - for cw in inputs.td_0.codewords() { + for cw in inputs.td_0_committed_codeword.codewords() { answers[qi][col] = cw[*idx]; col += 1; } @@ -209,7 +209,7 @@ where #[tracing::instrument( name = "proximity.verify", skip_all, - fields(t = statement.t, l2 = statement.l2) + fields(t = statement.t_num_queries, l2 = statement.l2_second_fold_factor) )] fn verify_inner<'b, 'c>( &self, @@ -223,7 +223,7 @@ where H: 'c, { // Arity check: number of accumulator openings must match l2. - (inputs.acc.len() == statement.l2) + (inputs.acc.len() == statement.l2_second_fold_factor) .then_some(()) .ok_or(VerifierError::NumL2Instances)?; diff --git a/src/protocol/iors/sample_queries.rs b/src/protocol/iors/sample_queries.rs index 8b9f0bf..5286c92 100644 --- a/src/protocol/iors/sample_queries.rs +++ b/src/protocol/iors/sample_queries.rs @@ -18,7 +18,7 @@ use crate::protocol::oracles::query_indices::QueryIndices; pub struct SampleQueriesStatement { pub log_n: usize, - pub t: usize, + pub t_num_queries: usize, } pub struct SampleQueriesReductionInputs { @@ -73,7 +73,7 @@ where } } - #[tracing::instrument(name = "sample_queries", skip_all, fields(t = statement.t, log_n = statement.log_n))] + #[tracing::instrument(name = "sample_queries", skip_all, fields(t = statement.t_num_queries, log_n = statement.log_n))] fn prove_inner<'b>( &self, prover_state: &mut ProverState, @@ -84,7 +84,8 @@ where where Self: 'b, { - let queries = QueryIndices::::sample(prover_state, statement.log_n, statement.t); + let queries = + QueryIndices::::sample(prover_state, statement.log_n, statement.t_num_queries); Ok((SampleQueriesReductionInputs { queries }, (), ())) } @@ -98,11 +99,12 @@ where where Self: 'c, { - let n_bytes = (statement.t * statement.log_n).div_ceil(8); + let n_bytes = (statement.t_num_queries * statement.log_n).div_ceil(8); let bytes: Vec = (0..n_bytes) .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) .collect(); - let queries = QueryIndices::from_squeezed_bytes(&bytes, statement.log_n, statement.t); + let queries = + QueryIndices::from_squeezed_bytes(&bytes, statement.log_n, statement.t_num_queries); Ok((SampleQueriesReductionInputs { queries }, ())) } } diff --git a/src/protocol/iors/twin_constraint.rs b/src/protocol/iors/twin_constraint.rs index cabc168..c9becda 100644 --- a/src/protocol/iors/twin_constraint.rs +++ b/src/protocol/iors/twin_constraint.rs @@ -155,8 +155,8 @@ impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { pub struct TwinConstraintStatement { pub acc_instance: AccumulatorInstance, - pub l1_mus: Vec, - pub l1_taus: Vec>, + pub l1_mus_codeword_first_coords: Vec, + pub l1_taus_zero_check_challenges: Vec>, pub log_l: usize, pub log_m: usize, pub log_n: usize, @@ -176,14 +176,15 @@ pub struct TwinConstraintProverInputs<'a, F: Field> { /// Deferred oracle check: `final_claim ≟ eq(τ,γ)·(ν₀ + ω·η)`. Cannot fire /// inside `verify` because ν₀, η arrive on the transcript only after Bridge. pub struct DeferredOracleCheck { - pub omega: F, - pub tau: Vec, + pub omega_zero_check_randomness: F, + pub tau_zero_check_challenges: Vec, pub claim: F, } impl DeferredOracleCheck { pub fn discharge(&self, gamma: &[F], nu_0: F, eta: F) -> Result<(), VerifierError> { - let expected = eq_poly_non_binary(&self.tau, gamma) * (nu_0 + self.omega * eta); + let expected = eq_poly_non_binary(&self.tau_zero_check_challenges, gamma) + * (nu_0 + self.omega_zero_check_randomness * eta); (expected == self.claim) .then_some(()) .ok_or(VerifierError::Target) @@ -191,22 +192,22 @@ impl DeferredOracleCheck { } pub struct TwinConstraintReducedStatement { - pub gamma: Vec, + pub gamma_sumcheck_challenges: Vec, pub zeta_0: Vec, pub beta_tau: Vec, pub deferred: DeferredOracleCheck, } pub struct TwinConstraintReductionInputs { - pub omega: F, - pub tau: Vec, - pub gamma: Vec, + pub omega_zero_check_randomness: F, + pub tau_zero_check_challenges: Vec, + pub gamma_sumcheck_challenges: Vec, pub final_claim: F, } pub struct TwinConstraintReducedWitness { - pub f: Oracle, - pub z: Vec, + pub f_oracle: Oracle, + pub z_witness_assignment: Vec, } pub struct TwinConstraint<'a, F, H> @@ -260,12 +261,12 @@ where { let log_l = statement.log_l; let log_n = statement.log_n; - let l1 = statement.l1_mus.len(); + let l1 = statement.l1_mus_codeword_first_coords.len(); - let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &inputs.gamma); + let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &inputs.gamma_sumcheck_challenges); let alpha_vecs = concat_slices( - &statement.acc_instance.alpha, + &statement.acc_instance.alpha_fold_vectors, &vec![vec![F::zero(); log_n]; l1], ); let zeta_0 = scale_and_sum(&alpha_vecs, &gamma_eq_evals); @@ -274,21 +275,21 @@ where // (length l1). The τ component of the new β = Σ γ_eq(i) · β_i. let beta_taus: Vec> = statement .acc_instance - .beta + .beta_twin_pairs .0 .iter() .cloned() - .chain(statement.l1_taus.iter().cloned()) + .chain(statement.l1_taus_zero_check_challenges.iter().cloned()) .collect(); let beta_tau = scale_and_sum(&beta_taus, &gamma_eq_evals); TwinConstraintReducedStatement { - gamma: inputs.gamma.clone(), + gamma_sumcheck_challenges: inputs.gamma_sumcheck_challenges.clone(), zeta_0, beta_tau, deferred: DeferredOracleCheck { - omega: inputs.omega, - tau: inputs.tau.clone(), + omega_zero_check_randomness: inputs.omega_zero_check_randomness, + tau_zero_check_challenges: inputs.tau_zero_check_challenges.clone(), claim: inputs.final_claim, }, } @@ -326,13 +327,13 @@ where .collect::>(); let alpha_vecs = concat_slices( - &statement.acc_instance.alpha, + &statement.acc_instance.alpha_fold_vectors, &vec![vec![F::zero(); log_n]; l1], ); let z_vecs: Vec> = statement .acc_instance - .beta + .beta_twin_pairs .1 .iter() .zip(witness.acc_witness_w) @@ -343,11 +344,11 @@ where // β tables: accumulated β-τs first, then PESAT τs. let beta_vecs: Vec> = statement .acc_instance - .beta + .beta_twin_pairs .0 .iter() .cloned() - .chain(statement.l1_taus.iter().cloned()) + .chain(statement.l1_taus_zero_check_challenges.iter().cloned()) .collect(); let tablewise = vec![ @@ -385,15 +386,15 @@ where Ok(( TwinConstraintReductionInputs { - omega, - tau, - gamma: proof.challenges, + omega_zero_check_randomness: omega, + tau_zero_check_challenges: tau, + gamma_sumcheck_challenges: proof.challenges, final_claim: proof.final_value, }, (), TwinConstraintReducedWitness { - f: Oracle::from_evals(f), - z, + f_oracle: Oracle::from_evals(f), + z_witness_assignment: z, }, )) } @@ -416,7 +417,7 @@ where { let log_l = statement.log_l; let log_n = statement.log_n; - let l1 = statement.l1_mus.len(); + let l1 = statement.l1_mus_codeword_first_coords.len(); // Squeeze ω, τ matching the prover. let omega: F = verifier_state.verifier_message(); @@ -426,16 +427,19 @@ where // Compute σ₁ = Σ_i τ_eq(i) · (μ_i + ω · η_i). let tau_eq_evals = compute_hypercube_eq_evals(log_l, &tau); - let etas_l2_first = concat_slices(&statement.acc_instance.eta, &vec![F::zero(); l1]); + let etas_l2_first = concat_slices( + &statement.acc_instance.eta_predicate_evals, + &vec![F::zero(); l1], + ); let sigma_1 = tau_eq_evals .into_iter() .zip( statement .acc_instance - .mu + .mu_claimed_evals .iter() .copied() - .chain(statement.l1_mus.iter().copied()) + .chain(statement.l1_mus_codeword_first_coords.iter().copied()) .zip(etas_l2_first), ) .fold(F::zero(), |acc, (eq_tau, (mu, eta))| { @@ -455,9 +459,9 @@ where Ok(( TwinConstraintReductionInputs { - omega, - tau, - gamma, + omega_zero_check_randomness: omega, + tau_zero_check_challenges: tau, + gamma_sumcheck_challenges: gamma, final_claim, }, (), diff --git a/src/protocol/transcript/prover.rs b/src/protocol/transcript/prover.rs index 476ffc3..b7109e2 100644 --- a/src/protocol/transcript/prover.rs +++ b/src/protocol/transcript/prover.rs @@ -24,33 +24,33 @@ where H::Digest: Encoding<[u8]>, { pub fn absorb_into(&self, prover_state: &mut ProverState) { - for digest in &self.rt { + for digest in &self.rt_merkle_roots { prover_state.prover_message(digest); } - for alpha in &self.alpha { + for alpha in &self.alpha_fold_vectors { for f in alpha { prover_state.prover_message(f); } } - for f in &self.mu { + for f in &self.mu_claimed_evals { prover_state.prover_message(f); } - for tau in &self.beta.0 { + for tau in &self.beta_twin_pairs.0 { for f in tau { prover_state.prover_message(f); } } - for x in &self.beta.1 { + for x in &self.beta_twin_pairs.1 { for f in x { prover_state.prover_message(f); } } - for f in &self.eta { + for f in &self.eta_predicate_evals { prover_state.prover_message(f); } } diff --git a/src/protocol/transcript/verifier.rs b/src/protocol/transcript/verifier.rs index 445a56f..201b5f0 100644 --- a/src/protocol/transcript/verifier.rs +++ b/src/protocol/transcript/verifier.rs @@ -66,11 +66,11 @@ where let eta: Vec = verifier_state.prover_messages_vec(l2)?; Ok(Self { - rt, - alpha, - mu, - beta: (taus, xs), - eta, + rt_merkle_roots: rt, + alpha_fold_vectors: alpha, + mu_claimed_evals: mu, + beta_twin_pairs: (taus, xs), + eta_predicate_evals: eta, }) } } diff --git a/src/relations/mod.rs b/src/relations/mod.rs index 998c24a..d15ac83 100644 --- a/src/relations/mod.rs +++ b/src/relations/mod.rs @@ -4,7 +4,6 @@ pub mod r1cs; pub use description::SerializableConstraintMatrices; use ark_ff::Field; -use r1cs::R1CS; use crate::error::WARPError; @@ -13,7 +12,7 @@ pub trait Relation { type Witness; type Config; fn constraints(&self) -> usize; - fn description(config: &Self::Config) -> Vec; + fn describe_from_config(config: &Self::Config) -> Vec; fn instance(&self) -> Self::Instance; fn new(instance: Self::Instance, witness: Self::Witness, config: Self::Config) -> Self; fn public_config(&self) -> Vec; @@ -23,16 +22,16 @@ pub trait Relation { fn witness(&self) -> Self::Witness; } -pub trait BundledPESAT { +pub trait PolyPredicate { type Config; - type Constraints; fn evaluate_bundled(&self, zero_evader_evals: &[F], z: &[F]) -> Result; fn config(&self) -> Self::Config; fn description(&self) -> Vec; - fn constraints(&self) -> &Self::Constraints; + fn constraints(&self) -> &r1cs::R1CSConstraints; } -pub trait ToPolySystem: Relation { - // generate an r1cs polynomial system ((A, B, C), M, N, k) for a relation - fn into_r1cs(config: &Self::Config) -> Result, WARPError>; +pub trait Arithmetize { + type Config; + type Predicate; + fn arithmetize(config: &Self::Config) -> Result; } diff --git a/src/relations/r1cs/hashchain/mod.rs b/src/relations/r1cs/hashchain/mod.rs index 4f34fe3..d6402e5 100644 --- a/src/relations/r1cs/hashchain/mod.rs +++ b/src/relations/r1cs/hashchain/mod.rs @@ -22,15 +22,18 @@ pub use witness::HashChainWitness; use super::R1CS; use crate::error::WARPError; -use crate::relations::ToPolySystem; +use crate::relations::Arithmetize; impl< F: PrimeField + Absorb, H: CRHScheme, HG: CRHSchemeGadget], OutputVar = FpVar>, - > ToPolySystem for HashChainRelation + > Arithmetize for HashChainRelation { - fn into_r1cs(config: &Self::Config) -> Result, WARPError> { + type Config = (H::Parameters, usize); + type Predicate = R1CS; + + fn arithmetize(config: &Self::Config) -> Result, WARPError> { let (params, hash_chain_size) = config; let preimage = vec![F::ZERO]; let digest = compute_hash_chain::(params, &preimage, *hash_chain_size); diff --git a/src/relations/r1cs/hashchain/relation.rs b/src/relations/r1cs/hashchain/relation.rs index e681158..3fd295c 100644 --- a/src/relations/r1cs/hashchain/relation.rs +++ b/src/relations/r1cs/hashchain/relation.rs @@ -56,7 +56,7 @@ where self.constraint_system.num_constraints() } - fn description(config: &Self::Config) -> Vec { + fn describe_from_config(config: &Self::Config) -> Vec { let (hash_config, hash_chain_size) = (config.0.clone(), config.1); let zero_witness = HashChainWitness:: { preimage: vec![F::zero()], @@ -208,7 +208,7 @@ mod tests { } #[test] - fn description() { + fn describe_from_config() { let hash_chain_size = 1; let zero_witness = HashChainWitness:: { preimage: vec![BLS12_381::zero()], @@ -229,7 +229,7 @@ mod tests { ); assert!(relation.verify()); let description: Vec = - HashChainRelation::::description(&( + HashChainRelation::::describe_from_config(&( initialize_poseidon_config(), hash_chain_size, )); diff --git a/src/relations/r1cs/mod.rs b/src/relations/r1cs/mod.rs index 2b23c04..3c0db85 100644 --- a/src/relations/r1cs/mod.rs +++ b/src/relations/r1cs/mod.rs @@ -7,7 +7,7 @@ use rayon::prelude::*; use crate::error::WARPError; use crate::relations::SerializableConstraintMatrices; -use super::BundledPESAT; +use super::PolyPredicate; pub type R1CSConstraints = Vec<(Vec<(F, usize)>, Vec<(F, usize)>, Vec<(F, usize)>)>; @@ -16,10 +16,10 @@ pub struct R1CS { // we access linear combinations using binary hypercube points // point -> (a_i, b_i, c_i) // point is encoded via the n least significant bits of a usize - pub p: R1CSConstraints, - pub m: usize, - pub n: usize, - pub k: usize, + pub constraints_vec: R1CSConstraints, + pub m_num_constraints: usize, + pub n_num_variables: usize, + pub k_num_witness_vars: usize, pub log_m: usize, pub log_n: usize, } @@ -63,10 +63,10 @@ impl TryFrom> for R1CS { } Ok(R1CS { - p, - m, - n, - k, + constraints_vec: p, + m_num_constraints: m, + n_num_variables: n, + k_num_witness_vars: k, log_m, log_n, }) @@ -87,7 +87,10 @@ impl R1CS { // eval the R1CS i-th linear combination, where i is represented as an hypercube point pub fn eval_p_i(&self, z: &[F], i: usize) -> Result { - let (a_i, b_i, c_i) = self.p.get(i).ok_or(WARPError::R1CSNonExistingLC)?; + let (a_i, b_i, c_i) = self + .constraints_vec + .get(i) + .ok_or(WARPError::R1CSNonExistingLC)?; let eval_a_i = Self::eval_lc(a_i, z)?; let eval_b_i = Self::eval_lc(b_i, z)?; let eval_c_i = Self::eval_lc(c_i, z)?; @@ -95,18 +98,17 @@ impl R1CS { } } -impl BundledPESAT for R1CS { +impl PolyPredicate for R1CS { type Config = (usize, usize, usize); - type Constraints = R1CSConstraints; fn evaluate_bundled(&self, zero_evader_evals: &[F], z: &[F]) -> Result { - if zero_evader_evals.len() < self.m { + if zero_evader_evals.len() < self.m_num_constraints { return Err(WARPError::ZeroEvaderSize( zero_evader_evals.len(), - self.m - 1, + self.m_num_constraints - 1, )); } - (0..self.m) + (0..self.m_num_constraints) .into_par_iter() .map(|i| -> Result { let p_i = self.eval_p_i(z, i)?; @@ -116,7 +118,11 @@ impl BundledPESAT for R1CS { } fn config(&self) -> Self::Config { - (self.m, self.n, self.k) + ( + self.m_num_constraints, + self.n_num_variables, + self.k_num_witness_vars, + ) } fn description(&self) -> Vec { @@ -126,13 +132,25 @@ impl BundledPESAT for R1CS { // `WARP::index` would only commit to dimensions — a soundness hole // in any downstream protocol that trusts `index()` to bind the // relation. - let a: Vec> = self.p.iter().map(|(a, _, _)| a.clone()).collect(); - let b: Vec> = self.p.iter().map(|(_, b, _)| b.clone()).collect(); - let c: Vec> = self.p.iter().map(|(_, _, c)| c.clone()).collect(); + let a: Vec> = self + .constraints_vec + .iter() + .map(|(a, _, _)| a.clone()) + .collect(); + let b: Vec> = self + .constraints_vec + .iter() + .map(|(_, b, _)| b.clone()) + .collect(); + let c: Vec> = self + .constraints_vec + .iter() + .map(|(_, _, c)| c.clone()) + .collect(); let serializable = SerializableConstraintMatrices { - num_instance_variables: self.n - self.k, - num_witness_variables: self.k, - num_constraints: self.m, + num_instance_variables: self.n_num_variables - self.k_num_witness_vars, + num_witness_variables: self.k_num_witness_vars, + num_constraints: self.m_num_constraints, a: SerializableConstraintMatrices::serialize_nested_field(a), b: SerializableConstraintMatrices::serialize_nested_field(b), c: SerializableConstraintMatrices::serialize_nested_field(c), @@ -142,7 +160,7 @@ impl BundledPESAT for R1CS { .into_bytes() } - fn constraints(&self) -> &Self::Constraints { - &self.p + fn constraints(&self) -> &R1CSConstraints { + &self.constraints_vec } } diff --git a/src/serialize.rs b/src/serialize.rs index 7b5e31a..b160c3d 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -23,22 +23,30 @@ where mut writer: W, compress: Compress, ) -> Result<(), SerializationError> { - self.rt.serialize_with_mode(&mut writer, compress)?; - self.alpha.serialize_with_mode(&mut writer, compress)?; - self.mu.serialize_with_mode(&mut writer, compress)?; - self.beta.0.serialize_with_mode(&mut writer, compress)?; - self.beta.1.serialize_with_mode(&mut writer, compress)?; - self.eta.serialize_with_mode(&mut writer, compress)?; + self.rt_merkle_roots + .serialize_with_mode(&mut writer, compress)?; + self.alpha_fold_vectors + .serialize_with_mode(&mut writer, compress)?; + self.mu_claimed_evals + .serialize_with_mode(&mut writer, compress)?; + self.beta_twin_pairs + .0 + .serialize_with_mode(&mut writer, compress)?; + self.beta_twin_pairs + .1 + .serialize_with_mode(&mut writer, compress)?; + self.eta_predicate_evals + .serialize_with_mode(&mut writer, compress)?; Ok(()) } fn serialized_size(&self, compress: Compress) -> usize { - self.rt.serialized_size(compress) - + self.alpha.serialized_size(compress) - + self.mu.serialized_size(compress) - + self.beta.0.serialized_size(compress) - + self.beta.1.serialized_size(compress) - + self.eta.serialized_size(compress) + self.rt_merkle_roots.serialized_size(compress) + + self.alpha_fold_vectors.serialized_size(compress) + + self.mu_claimed_evals.serialized_size(compress) + + self.beta_twin_pairs.0.serialized_size(compress) + + self.beta_twin_pairs.1.serialized_size(compress) + + self.eta_predicate_evals.serialized_size(compress) } } @@ -65,10 +73,14 @@ where mut writer: W, compress: Compress, ) -> Result<(), SerializationError> { - self.rt_0.serialize_with_mode(&mut writer, compress)?; - self.mu_i.serialize_with_mode(&mut writer, compress)?; - self.nu_0.serialize_with_mode(&mut writer, compress)?; - self.nu_i.serialize_with_mode(&mut writer, compress)?; + self.rt_0_fresh_merkle_root + .serialize_with_mode(&mut writer, compress)?; + self.mu_i_first_codeword_coords + .serialize_with_mode(&mut writer, compress)?; + self.nu_0_oracle_eval + .serialize_with_mode(&mut writer, compress)?; + self.nu_i_oracle_evals + .serialize_with_mode(&mut writer, compress)?; self.auth_0.serialize_with_mode(&mut writer, compress)?; self.auth_j.serialize_with_mode(&mut writer, compress)?; self.shift_query_answers @@ -77,10 +89,10 @@ where } fn serialized_size(&self, compress: Compress) -> usize { - self.rt_0.serialized_size(compress) - + self.mu_i.serialized_size(compress) - + self.nu_0.serialized_size(compress) - + self.nu_i.serialized_size(compress) + self.rt_0_fresh_merkle_root.serialized_size(compress) + + self.mu_i_first_codeword_coords.serialized_size(compress) + + self.nu_0_oracle_eval.serialized_size(compress) + + self.nu_i_oracle_evals.serialized_size(compress) + self.auth_0.serialized_size(compress) + self.auth_j.serialized_size(compress) + self.shift_query_answers.serialized_size(compress) @@ -108,5 +120,5 @@ where F: Field + CanonicalSerialize, H: MerkleHasher>, { - acc_witness.w.serialized_size(compress) + acc_witness.w_witnesses.serialized_size(compress) } diff --git a/src/warp/accumulator.rs b/src/warp/accumulator.rs index 35cbc50..3dc503b 100644 --- a/src/warp/accumulator.rs +++ b/src/warp/accumulator.rs @@ -6,31 +6,31 @@ use crate::crypto::merkle::WarpCommitted; /// Public part of an accumulated claim: `(rt, α, μ, (τ, x), η)` in the paper. #[derive(Clone)] pub struct AccumulatorInstance { - pub rt: Vec, - pub alpha: Vec>, - pub mu: Vec, - pub beta: (Vec>, Vec>), - pub eta: Vec, + pub rt_merkle_roots: Vec, + pub alpha_fold_vectors: Vec>, + pub mu_claimed_evals: Vec, + pub beta_twin_pairs: (Vec>, Vec>), + pub eta_predicate_evals: Vec, } impl AccumulatorInstance { pub fn empty() -> Self { Self { - rt: vec![], - alpha: vec![], - mu: vec![], - beta: (vec![], vec![]), - eta: vec![], + rt_merkle_roots: vec![], + alpha_fold_vectors: vec![], + mu_claimed_evals: vec![], + beta_twin_pairs: (vec![], vec![]), + eta_predicate_evals: vec![], } } pub fn extend(mut self, other: Self) -> Self { - self.rt.extend(other.rt); - self.alpha.extend(other.alpha); - self.mu.extend(other.mu); - self.beta.0.extend(other.beta.0); - self.beta.1.extend(other.beta.1); - self.eta.extend(other.eta); + self.rt_merkle_roots.extend(other.rt_merkle_roots); + self.alpha_fold_vectors.extend(other.alpha_fold_vectors); + self.mu_claimed_evals.extend(other.mu_claimed_evals); + self.beta_twin_pairs.0.extend(other.beta_twin_pairs.0); + self.beta_twin_pairs.1.extend(other.beta_twin_pairs.1); + self.eta_predicate_evals.extend(other.eta_predicate_evals); self } } @@ -41,8 +41,8 @@ where F: Field, H: MerkleHasher>, { - pub td: Vec>, - pub w: Vec>, + pub td_committed_codewords: Vec>, + pub w_witnesses: Vec>, } impl Clone for AccumulatorWitness @@ -53,8 +53,8 @@ where { fn clone(&self) -> Self { Self { - td: self.td.clone(), - w: self.w.clone(), + td_committed_codewords: self.td_committed_codewords.clone(), + w_witnesses: self.w_witnesses.clone(), } } } @@ -66,14 +66,15 @@ where { pub fn empty() -> Self { Self { - td: vec![], - w: vec![], + td_committed_codewords: vec![], + w_witnesses: vec![], } } pub fn extend(mut self, other: Self) -> Self { - self.td.extend(other.td); - self.w.extend(other.w); + self.td_committed_codewords + .extend(other.td_committed_codewords); + self.w_witnesses.extend(other.w_witnesses); self } } diff --git a/src/warp/decide.rs b/src/warp/decide.rs index 7bd5101..33526e8 100644 --- a/src/warp/decide.rs +++ b/src/warp/decide.rs @@ -8,14 +8,14 @@ use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize}; use crate::crypto::merkle::warp_scheme; use crate::error::{DeciderError, WARPError}; -use crate::relations::{r1cs::R1CSConstraints, BundledPESAT}; +use crate::relations::PolyPredicate; use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; use crate::warp::scheme::WARP; impl WARP where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - P: Clone + BundledPESAT, Config = (usize, usize, usize)>, + P: Clone + PolyPredicate, C: LinearCode + Clone, H: MerkleHasher>, H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, @@ -25,19 +25,19 @@ where acc_witness: AccumulatorWitness, acc_instance: AccumulatorInstance, ) -> Result<(), WARPError> { - let acc_codeword = &acc_witness.td[0].codewords()[0]; + let acc_codeword = &acc_witness.td_committed_codewords[0].codewords()[0]; - let computed_f = self.params.code.encode(&acc_witness.w[0]); + let computed_f = self.params.code.encode(&acc_witness.w_witnesses[0]); (acc_codeword == &computed_f) .then_some(()) .ok_or(DeciderError::EncodedWitness)?; let scheme = warp_scheme::(self.params.hasher.clone(), self.params.code.code_len()); let recomputed = scheme.commit(std::slice::from_ref(&computed_f)); - (acc_instance.rt[0] == *recomputed.root()) + (acc_instance.rt_merkle_roots[0] == *recomputed.root()) .then_some(()) .ok_or(DeciderError::MerkleRoot)?; - (acc_witness.td[0].root() == recomputed.root()) + (acc_witness.td_committed_codewords[0].root() == recomputed.root()) .then_some(()) .ok_or(DeciderError::MerkleTrapDoor)?; @@ -45,20 +45,20 @@ where log2(self.params.code.code_len()) as usize, acc_codeword, ); - (f_hat.evaluate(&acc_instance.alpha[0]) == acc_instance.mu[0]) + (f_hat.evaluate(&acc_instance.alpha_fold_vectors[0]) == acc_instance.mu_claimed_evals[0]) .then_some(()) .ok_or(DeciderError::MLExtensionEvaluation)?; - let tau = &acc_instance.beta.0[0]; + let tau = &acc_instance.beta_twin_pairs.0[0]; let tau_zero_evader = compute_hypercube_eq_evals(tau.len(), tau); - let mut z = acc_instance.beta.1[0].clone(); - z.extend(acc_witness.w[0].clone()); + let mut z = acc_instance.beta_twin_pairs.1[0].clone(); + z.extend(acc_witness.w_witnesses[0].clone()); let computed_eta = self .params - .p + .predicate .evaluate_bundled(&tau_zero_evader, &z) .unwrap(); - (computed_eta == acc_instance.eta[0]) + (computed_eta == acc_instance.eta_predicate_evals[0]) .then_some(()) .ok_or(DeciderError::BundledEvaluation)?; diff --git a/src/warp/keys.rs b/src/warp/keys.rs index bddc1c3..2e095cf 100644 --- a/src/warp/keys.rs +++ b/src/warp/keys.rs @@ -2,15 +2,15 @@ #[derive(Clone)] pub struct WARPProverKey

    { pub index: P, - pub m: usize, - pub n: usize, - pub k: usize, + pub m_num_constraints: usize, + pub n_num_variables: usize, + pub k_num_witness_vars: usize, } /// Verifier key — dimensions only `(M, N, k)`. #[derive(Clone, Copy)] pub struct WARPVerifierKey { - pub m: usize, - pub n: usize, - pub k: usize, + pub m_num_constraints: usize, + pub n_num_variables: usize, + pub k_num_witness_vars: usize, } diff --git a/src/warp/params.rs b/src/warp/params.rs index f2347b4..0481c68 100644 --- a/src/warp/params.rs +++ b/src/warp/params.rs @@ -4,13 +4,13 @@ use ark_mt::MerkleHasher; use std::marker::PhantomData; use crate::config::WARPConfig; -use crate::relations::BundledPESAT; +use crate::relations::PolyPredicate; /// Shared configuration used by all IORs. -pub struct WARPParams, C: LinearCode + Clone, H: MerkleHasher> { - pub(crate) _f: PhantomData, +pub struct WARPParams, C: LinearCode + Clone, H: MerkleHasher> { + pub(crate) _phantom_f: PhantomData, pub config: WARPConfig, pub code: C, - pub p: P, + pub predicate: P, pub hasher: H, } diff --git a/src/warp/proof.rs b/src/warp/proof.rs index a859649..e1832a4 100644 --- a/src/warp/proof.rs +++ b/src/warp/proof.rs @@ -8,10 +8,10 @@ use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; /// Proof produced by the WARP accumulation prover. Corresponds to /// `(rt₀, μᵢ, ν₀, νᵢ, auth₀, authⱼ, f_i(x_j))` in the paper. pub struct WARPProof { - pub rt_0: H::Digest, - pub mu_i: Vec, - pub nu_0: F, - pub nu_i: Vec, + pub rt_0_fresh_merkle_root: H::Digest, + pub mu_i_first_codeword_coords: Vec, + pub nu_0_oracle_eval: F, + pub nu_i_oracle_evals: Vec, pub auth_0: WarpProof, pub auth_j: Vec>, pub shift_query_answers: Vec>, @@ -26,10 +26,10 @@ where { fn clone(&self) -> Self { Self { - rt_0: self.rt_0.clone(), - mu_i: self.mu_i.clone(), - nu_0: self.nu_0, - nu_i: self.nu_i.clone(), + rt_0_fresh_merkle_root: self.rt_0_fresh_merkle_root.clone(), + mu_i_first_codeword_coords: self.mu_i_first_codeword_coords.clone(), + nu_0_oracle_eval: self.nu_0_oracle_eval, + nu_i_oracle_evals: self.nu_i_oracle_evals.clone(), auth_0: self.auth_0.clone(), auth_j: self.auth_j.clone(), shift_query_answers: self.shift_query_answers.clone(), diff --git a/src/warp/prove.rs b/src/warp/prove.rs index b08bfbc..68735d5 100644 --- a/src/warp/prove.rs +++ b/src/warp/prove.rs @@ -27,7 +27,7 @@ use crate::protocol::iors::{ }; use crate::protocol::transcript::absorb_instances; use crate::prove_ior; -use crate::relations::{r1cs::R1CSConstraints, BundledPESAT}; +use crate::relations::PolyPredicate; use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; use crate::warp::keys::WARPProverKey; use crate::warp::proof::{ProveResult, WARPProof}; @@ -36,21 +36,19 @@ use crate::warp::scheme::WARP; impl WARP where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - P: Clone + BundledPESAT, Config = (usize, usize, usize)>, + P: Clone + PolyPredicate, C: LinearCode + Clone, H: MerkleHasher>, H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, { - #[tracing::instrument(name = "warp.prove", skip_all)] - pub fn prove( + fn validate_prover_inputs( &self, - pk: WARPProverKey

    , - prover_state: &mut ProverState, - witnesses: Vec>, - instances: Vec>, - acc_instance: AccumulatorInstance, - acc_witness: AccumulatorWitness, - ) -> ProveResult { + pk: &WARPProverKey

    , + instances: &[Vec], + witnesses: &[Vec], + acc_instance: &AccumulatorInstance, + acc_witness: &AccumulatorWitness, + ) -> Result<(), ProverError> { if instances.len() < 2 { return Err(ProverError::InsufficientInstances { got: instances.len(), @@ -62,39 +60,54 @@ where witnesses: witnesses.len(), }); } - if acc_witness.td.len() != acc_instance.rt.len() { + if acc_witness.td_committed_codewords.len() != acc_instance.rt_merkle_roots.len() { return Err(ProverError::AccumulatorShapeMismatch { - instances: acc_instance.rt.len(), - roots: acc_witness.td.len(), + instances: acc_instance.rt_merkle_roots.len(), + roots: acc_witness.td_committed_codewords.len(), }); } - - let (l1, l) = (self.params.config.l1, self.params.config.l); - let l2 = l - l1; + let l = self.params.config.l_total_fold_factor(); if !l.is_power_of_two() { return Err(ProverError::ConfigParameterInvalid { - reason: format!("config.l = {l} is not a power of two"), + reason: format!( + "l1_first_fold_factor + l2_second_fold_factor = {l} is not a power of two" + ), }); } - - #[allow(non_snake_case)] - let (M, N, k) = (pk.m, pk.n, pk.k); - let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); - let n = self.params.code.code_len(); - let log_n = log2(n) as usize; - - if instances[0].len() != N - k { + let expected_instance_len = pk.n_num_variables - pk.k_num_witness_vars; + if instances[0].len() != expected_instance_len { return Err(ProverError::InstanceLengthMismatch { - expected: N - k, + expected: expected_instance_len, got: instances[0].len(), }); } + Ok(()) + } + + #[tracing::instrument(name = "warp.prove", skip_all)] + pub fn prove( + &self, + pk: WARPProverKey

    , + prover_state: &mut ProverState, + witnesses: Vec>, + instances: Vec>, + acc_instance: AccumulatorInstance, + acc_witness: AccumulatorWitness, + ) -> ProveResult { + self.validate_prover_inputs(&pk, &instances, &witnesses, &acc_instance, &acc_witness)?; + + let log_l = log2(self.params.config.l_total_fold_factor()) as usize; + let log_m = log2(pk.m_num_constraints) as usize; + let n_code_len = self.params.code.code_len(); + let log_n = log2(n_code_len) as usize; + let n_minus_k = pk.n_num_variables - pk.k_num_witness_vars; + absorb_instances(prover_state, &instances); acc_instance.absorb_into(prover_state); let AccumulatorWitness { - td: acc_tds, - w: acc_ws, + td_committed_codewords: acc_tds, + w_witnesses: acc_ws, } = acc_witness; let acc_fs: Vec> = acc_tds.iter().map(|td| td.codewords()[0].clone()).collect(); @@ -104,7 +117,7 @@ where _phantom: PhantomData, }; let twin_constraint_ior = TwinConstraint:: { - r1cs: self.params.p.constraints(), + r1cs: self.params.predicate.constraints(), _phantom: PhantomData, }; let bridge_ior = Bridge::::default(); @@ -117,13 +130,24 @@ where }; let IorProveResult { - reduced: PesatReducedStatement { mus, taus }, + reduced: + PesatReducedStatement { + mus_codeword_first_coords, + taus_zero_check_challenges, + }, proof: _, - witness: PesatReducedWitness { codewords, td_0 }, + witness: + PesatReducedWitness { + codewords, + td_0_committed_codeword, + }, } = prove_ior!( pesat_ior, prover_state, - statement: PesatStatement { l1, log_m }, + statement: PesatStatement { + l1_first_fold_factor: self.params.config.l1_first_fold_factor, + log_m, + }, witness: PesatWitness { witnesses: &witnesses }, inputs: (), )?; @@ -131,20 +155,24 @@ where let IorProveResult { reduced: TwinConstraintReducedStatement { - gamma: _, + gamma_sumcheck_challenges: _, zeta_0, beta_tau, deferred: _, }, proof: _, - witness: TwinConstraintReducedWitness { f, z }, + witness: + TwinConstraintReducedWitness { + f_oracle, + z_witness_assignment, + }, } = prove_ior!( twin_constraint_ior, prover_state, statement: TwinConstraintStatement { acc_instance, - l1_mus: mus.clone(), - l1_taus: taus, + l1_mus_codeword_first_coords: mus_codeword_first_coords.clone(), + l1_taus_zero_check_challenges: taus_zero_check_challenges, log_l, log_m, log_n, @@ -163,8 +191,8 @@ where let IorProveResult { reduced: BridgeReducedStatement { - eta, - nu_0, + eta_predicate_eval, + nu_0_oracle_eval, td_new_root: _, }, proof: _, @@ -181,13 +209,16 @@ where zeta_0: zeta_0.clone(), beta_tau: beta_tau.clone(), log_m, - n_minus_k: N - k, + n_minus_k, + }, + witness: BridgeWitness { + z_witness_assignment: &z_witness_assignment, + f_oracle: &f_oracle, }, - witness: BridgeWitness { z: &z, f: &f }, inputs: BridgeProverInputs { - bundled_pesat: &self.params.p, + predicate: &self.params.predicate, hasher: &self.params.hasher, - code_len: n, + code_len: n_code_len, _f: PhantomData, }, )?; @@ -203,9 +234,9 @@ where } = prove_ior!( ood_ior, prover_state, - statement: OodStatement { s: self.params.config.s, log_n }, + statement: OodStatement { s_num_ood_samples: self.params.config.s_num_ood_samples, log_n }, witness: (), - inputs: OodProverInputs { oracle: &f }, + inputs: OodProverInputs { oracle: &f_oracle }, )?; let IorProveResult { @@ -215,15 +246,18 @@ where } = prove_ior!( sample_queries_ior, prover_state, - statement: SampleQueriesStatement { log_n, t: self.params.config.t }, + statement: SampleQueriesStatement { log_n, t_num_queries: self.params.config.t_num_queries }, witness: (), inputs: (), )?; let IorProveResult { - reduced: BatchingReducedStatement { alpha }, + reduced: + BatchingReducedStatement { + alpha_sumcheck_challenges, + }, proof: _, - witness: BatchingReducedWitness { mu }, + witness: BatchingReducedWitness { mu_claimed_eval }, } = prove_ior!( batching_ior, prover_state, @@ -231,12 +265,12 @@ where zeta_0.clone(), &samples_flat, &queries.evaluation_points, - self.params.config.s, - self.params.config.t, + self.params.config.s_num_ood_samples, + self.params.config.t_num_queries, log_n, ), witness: (), - inputs: BatchingProverInputs { oracle: &f }, + inputs: BatchingProverInputs { oracle: &f_oracle }, )?; let IorProveResult { @@ -253,37 +287,37 @@ where prover_state, statement: ProximityStatement { queries: queries.clone(), - l2, - t: self.params.config.t, - n, + l2_second_fold_factor: self.params.config.l2_second_fold_factor, + t_num_queries: self.params.config.t_num_queries, + n_code_len, }, witness: (), inputs: ProximityProverInputs { - td_0: &td_0, - acc_td: &acc_tds, + td_0_committed_codeword: &td_0_committed_codeword, + acc_td_committed_codewords: &acc_tds, }, )?; - let mut nus = Vec::with_capacity(1 + self.params.config.s); - nus.push(nu_0); - nus.extend(answers); + let mut nu_i_oracle_evals = Vec::with_capacity(1 + self.params.config.s_num_ood_samples); + nu_i_oracle_evals.push(nu_0_oracle_eval); + nu_i_oracle_evals.extend(answers); let new_acc_instance = AccumulatorInstance { - rt: vec![td_new.root().clone()], - alpha: vec![alpha], - mu: vec![mu], - beta: (vec![beta_tau], vec![new_x]), - eta: vec![eta], + rt_merkle_roots: vec![td_new.root().clone()], + alpha_fold_vectors: vec![alpha_sumcheck_challenges], + mu_claimed_evals: vec![mu_claimed_eval], + beta_twin_pairs: (vec![beta_tau], vec![new_x]), + eta_predicate_evals: vec![eta_predicate_eval], }; let new_acc_witness = AccumulatorWitness { - td: vec![td_new], - w: vec![new_w], + td_committed_codewords: vec![td_new], + w_witnesses: vec![new_w], }; let proof = WARPProof { - rt_0: td_0.root().clone(), - mu_i: mus, - nu_0, - nu_i: nus, + rt_0_fresh_merkle_root: td_0_committed_codeword.root().clone(), + mu_i_first_codeword_coords: mus_codeword_first_coords, + nu_0_oracle_eval, + nu_i_oracle_evals, auth_0, auth_j, shift_query_answers, diff --git a/src/warp/scheme.rs b/src/warp/scheme.rs index 2f653f5..3420d8b 100644 --- a/src/warp/scheme.rs +++ b/src/warp/scheme.rs @@ -7,28 +7,28 @@ use spongefish::{ use std::marker::PhantomData; use crate::config::WARPConfig; -use crate::relations::BundledPESAT; +use crate::relations::PolyPredicate; use crate::warp::keys::{WARPProverKey, WARPVerifierKey}; use crate::warp::params::WARPParams; -pub struct WARP, C: LinearCode + Clone, H: MerkleHasher> { +pub struct WARP, C: LinearCode + Clone, H: MerkleHasher> { pub params: WARPParams, } impl WARP where F: Field, - P: Clone + BundledPESAT, + P: Clone + PolyPredicate, C: LinearCode + Clone, H: MerkleHasher>, { - pub fn new(config: WARPConfig, code: C, p: P, hasher: H) -> WARP { + pub fn new(config: WARPConfig, code: C, predicate: P, hasher: H) -> WARP { Self { params: WARPParams { - _f: PhantomData, + _phantom_f: PhantomData, config, code, - p, + predicate, hasher, }, } @@ -38,7 +38,7 @@ where impl WARP where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - P: Clone + BundledPESAT, + P: Clone + PolyPredicate, C: LinearCode + Clone, H: MerkleHasher>, { @@ -52,8 +52,17 @@ where prover_state.prover_message(&F::from(n as u32)); prover_state.prover_message(&F::from(k as u32)); Ok(( - WARPProverKey { index, m, n, k }, - WARPVerifierKey { m, n, k }, + WARPProverKey { + index, + m_num_constraints: m, + n_num_variables: n, + k_num_witness_vars: k, + }, + WARPVerifierKey { + m_num_constraints: m, + n_num_variables: n, + k_num_witness_vars: k, + }, )) } } diff --git a/src/warp/verify.rs b/src/warp/verify.rs index 733354d..04a2a9f 100644 --- a/src/warp/verify.rs +++ b/src/warp/verify.rs @@ -20,7 +20,7 @@ use crate::protocol::iors::{ }; use crate::protocol::oracles::indexed_merkle::MerkleIndexedOracle; use crate::protocol::transcript::parse_statement; -use crate::relations::{r1cs::R1CSConstraints, BundledPESAT}; +use crate::relations::PolyPredicate; use crate::utils::{concat_slices, scale_and_sum}; use crate::verify_ior; use crate::warp::accumulator::AccumulatorInstance; @@ -31,7 +31,7 @@ use crate::warp::scheme::WARP; impl WARP where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - P: Clone + BundledPESAT, Config = (usize, usize, usize)>, + P: Clone + PolyPredicate, C: LinearCode + Clone, H: MerkleHasher>, H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, @@ -43,25 +43,28 @@ where acc_instance: AccumulatorInstance, proof: WARPProof, ) -> Result<(), VerifierError> { - let (l1, l) = (self.params.config.l1, self.params.config.l); - let l2 = l - l1; + let log_l = log2(self.params.config.l_total_fold_factor()) as usize; + let log_m = log2(vk.m_num_constraints) as usize; + let n_code_len = self.params.code.code_len(); + let log_n = log2(n_code_len) as usize; + let n_minus_k = vk.n_num_variables - vk.k_num_witness_vars; - #[allow(non_snake_case)] - let (M, N, k) = (vk.m, vk.n, vk.k); - let (log_m, log_l) = (log2(M) as usize, log2(l) as usize); - let n = self.params.code.code_len(); - let log_n = log2(n) as usize; - - let (l1_xs, parsed_acc) = - parse_statement::(verifier_state, l1, l2, N - k, log_n, log_m)?; + let (l1_xs, parsed_acc) = parse_statement::( + verifier_state, + self.params.config.l1_first_fold_factor, + self.params.config.l2_second_fold_factor, + n_minus_k, + log_n, + log_m, + )?; - let acc_alpha_first = acc_instance.alpha[0].clone(); - let acc_beta_0_first = acc_instance.beta.0[0].clone(); - let acc_beta_1_first = acc_instance.beta.1[0].clone(); - let acc_mu_first = acc_instance.mu[0]; - let l2_taus = parsed_acc.beta.0.clone(); - let l2_xs = parsed_acc.beta.1.clone(); - let l2_roots = parsed_acc.rt.clone(); + let acc_alpha_first = acc_instance.alpha_fold_vectors[0].clone(); + let acc_beta_0_first = acc_instance.beta_twin_pairs.0[0].clone(); + let acc_beta_1_first = acc_instance.beta_twin_pairs.1[0].clone(); + let acc_mu_first = acc_instance.mu_claimed_evals[0]; + let l2_taus = parsed_acc.beta_twin_pairs.0.clone(); + let l2_xs = parsed_acc.beta_twin_pairs.1.clone(); + let l2_roots = parsed_acc.rt_merkle_roots.clone(); let pesat_ior = Pesat:: { code: &self.params.code, @@ -69,7 +72,7 @@ where _phantom: PhantomData, }; let twin_constraint_ior = TwinConstraint:: { - r1cs: self.params.p.constraints(), + r1cs: self.params.predicate.constraints(), _phantom: PhantomData, }; let bridge_ior = Bridge::::default(); @@ -84,21 +87,23 @@ where let IorVerifyResult { reduced: PesatReducedStatement { - mus: l1_mus, - taus: l1_taus, + mus_codeword_first_coords, + taus_zero_check_challenges, }, - outputs: PesatVerifierOutputs { rt_0 }, + outputs: PesatVerifierOutputs { + rt_0_fresh_merkle_root, + }, } = verify_ior!( pesat_ior, verifier_state, - statement: PesatStatement { l1, log_m }, + statement: PesatStatement { l1_first_fold_factor: self.params.config.l1_first_fold_factor, log_m }, inputs: (), )?; let IorVerifyResult { reduced: TwinConstraintReducedStatement { - gamma, + gamma_sumcheck_challenges, zeta_0, beta_tau, deferred, @@ -109,8 +114,8 @@ where verifier_state, statement: TwinConstraintStatement { acc_instance: parsed_acc, - l1_mus: l1_mus.clone(), - l1_taus: l1_taus.clone(), + l1_mus_codeword_first_coords: mus_codeword_first_coords.clone(), + l1_taus_zero_check_challenges: taus_zero_check_challenges.clone(), log_l, log_m, log_n, @@ -121,8 +126,8 @@ where let IorVerifyResult { reduced: BridgeReducedStatement { - eta: _, - nu_0, + eta_predicate_eval: _, + nu_0_oracle_eval, td_new_root: _, }, outputs: _, @@ -133,11 +138,11 @@ where zeta_0: zeta_0.clone(), beta_tau, log_m, - n_minus_k: N - k, + n_minus_k, }, inputs: BridgeVerifierInputs { deferred: &deferred, - gamma: &gamma, + gamma_twin_constraint_challenges: &gamma_sumcheck_challenges, }, )?; @@ -151,7 +156,7 @@ where } = verify_ior!( ood_ior, verifier_state, - statement: OodStatement { s: self.params.config.s, log_n }, + statement: OodStatement { s_num_ood_samples: self.params.config.s_num_ood_samples, log_n }, inputs: (), )?; @@ -161,14 +166,14 @@ where } = verify_ior!( sample_queries_ior, verifier_state, - statement: SampleQueriesStatement { log_n, t: self.params.config.t }, + statement: SampleQueriesStatement { log_n, t_num_queries: self.params.config.t_num_queries }, inputs: (), )?; - (proof.shift_query_answers.len() == self.params.config.t) + (proof.shift_query_answers.len() == self.params.config.t_num_queries) .then_some(()) .ok_or(VerifierError::NumShiftQueries)?; - (proof.auth_j.len() == l2) + (proof.auth_j.len() == self.params.config.l2_second_fold_factor) .then_some(()) .ok_or(VerifierError::NumL2Instances)?; @@ -186,40 +191,43 @@ where let fresh_values: Vec> = row_indices .iter() - .map(|&r| proof.shift_query_answers[r][l2..].to_vec()) + .map(|&r| { + proof.shift_query_answers[r][self.params.config.l2_second_fold_factor..].to_vec() + }) .collect(); let fresh_handle = MerkleIndexedOracle::new( - warp_scheme::(self.params.hasher.clone(), n), - &rt_0, + warp_scheme::(self.params.hasher.clone(), n_code_len), + &rt_0_fresh_merkle_root, &proof.auth_0, sorted_unique.clone(), fresh_values, ); - let acc_handles: Vec> = (0..l2) - .map(|j| { - let acc_values: Vec> = row_indices - .iter() - .map(|&r| vec![proof.shift_query_answers[r][j]]) - .collect(); - MerkleIndexedOracle::new( - warp_scheme::(self.params.hasher.clone(), n), - &l2_roots[j], - &proof.auth_j[j], - sorted_unique.clone(), - acc_values, - ) - }) - .collect(); + let acc_handles: Vec> = + (0..self.params.config.l2_second_fold_factor) + .map(|j| { + let acc_values: Vec> = row_indices + .iter() + .map(|&r| vec![proof.shift_query_answers[r][j]]) + .collect(); + MerkleIndexedOracle::new( + warp_scheme::(self.params.hasher.clone(), n_code_len), + &l2_roots[j], + &proof.auth_j[j], + sorted_unique.clone(), + acc_values, + ) + }) + .collect(); verify_ior!( proximity_ior, verifier_state, statement: ProximityStatement { queries: queries.clone(), - l2, - t: self.params.config.t, - n, + l2_second_fold_factor: self.params.config.l2_second_fold_factor, + t_num_queries: self.params.config.t_num_queries, + n_code_len, }, inputs: ProximityVerifierInputs { fresh: &fresh_handle, @@ -228,20 +236,25 @@ where }, )?; - let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma); - let mut nus = Vec::with_capacity(1 + self.params.config.s + self.params.config.t); - nus.push(nu_0); - nus.extend(answers); + let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma_sumcheck_challenges); + let mut nu_i_oracle_evals = Vec::with_capacity( + 1 + self.params.config.s_num_ood_samples + self.params.config.t_num_queries, + ); + nu_i_oracle_evals.push(nu_0_oracle_eval); + nu_i_oracle_evals.extend(answers); for v_jk in proof.shift_query_answers.iter() { let nu_st = v_jk .iter() .zip(&gamma_eq_evals) .fold(F::zero(), |acc, (v, eq)| acc + *eq * *v); - nus.push(nu_st); + nu_i_oracle_evals.push(nu_st); } let IorVerifyResult { - reduced: BatchingReducedStatement { alpha }, + reduced: + BatchingReducedStatement { + alpha_sumcheck_challenges, + }, outputs: _, } = verify_ior!( batching_ior, @@ -250,23 +263,23 @@ where zeta_0.clone(), &samples_flat, &queries.evaluation_points, - self.params.config.s, - self.params.config.t, + self.params.config.s_num_ood_samples, + self.params.config.t_num_queries, log_n, ), inputs: BatchingVerifierInputs { - nus, + nus_claimed_evals: nu_i_oracle_evals, acc_mu: acc_mu_first, }, )?; - (acc_alpha_first == alpha) + (acc_alpha_first == alpha_sumcheck_challenges) .then_some(()) .ok_or(VerifierError::CodeEvaluationPoint)?; let betas = l2_taus .into_iter() - .chain(l1_taus) + .chain(taus_zero_check_challenges) .zip(l2_xs.into_iter().chain(l1_xs)) .map(|(tau_i, x)| concat_slices(&tau_i, &x)) .collect::>>(); diff --git a/tests/integration_warp.rs b/tests/integration_warp.rs index 42465b6..2a7b1ef 100644 --- a/tests/integration_warp.rs +++ b/tests/integration_warp.rs @@ -29,7 +29,7 @@ use warp::relations::{ hashchain::{compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness}, R1CS, }, - BundledPESAT, Relation, ToPolySystem, + Arithmetize, PolyPredicate, Relation, }; use warp::serialize::acc_witness_size; use warp::utils::poseidon; @@ -44,12 +44,15 @@ fn warp_test() { let hash_chain_size = 10; let mut rng = thread_rng(); let poseidon_config = poseidon::initialize_poseidon_config::(); - let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + let r1cs = HashChainRelation::, CRHGadget<_>>::arithmetize(&( poseidon_config.clone(), hash_chain_size, )) .unwrap(); - let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code_config = ReedSolomonConfig::::default( + r1cs.k_num_witness_vars, + r1cs.k_num_witness_vars.next_power_of_two(), + ); let code = ReedSolomon::new(code_config.clone()); let instances_witnesses: (Vec>, Vec>) = (0..l1) @@ -72,13 +75,13 @@ fn warp_test() { }) .unzip(); - let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + let r1cs = HashChainRelation::, CRHGadget<_>>::arithmetize(&( poseidon_config.clone(), hash_chain_size, )) .unwrap(); - let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); + let warp_config = WARPConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); let hash_chain_warp = WARP::, _, Blake3FieldHasher>::new( warp_config.clone(), code.clone(), @@ -96,9 +99,9 @@ fn warp_test() { .prove( WARPProverKey { index: r1cs.clone(), - m: r1cs.m, - n: r1cs.n, - k: r1cs.k, + m_num_constraints: r1cs.m_num_constraints, + n_num_variables: r1cs.n_num_variables, + k_num_witness_vars: r1cs.k_num_witness_vars, }, &mut prover_state, instances_witnesses.1.clone(), @@ -113,7 +116,7 @@ fn warp_test() { let domainsep = spongefish::domain_separator!("test::warp"); let warp_config = - WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); + WARPConfig::<_, R1CS>::new(l1, 4, s, t, r1cs.config(), code.code_len()); let hash_chain_warp = WARP::, _, Blake3FieldHasher>::new( warp_config.clone(), @@ -127,9 +130,9 @@ fn warp_test() { .prove( WARPProverKey { index: r1cs.clone(), - m: r1cs.m, - n: r1cs.n, - k: r1cs.k, + m_num_constraints: r1cs.m_num_constraints, + n_num_variables: r1cs.n_num_variables, + k_num_witness_vars: r1cs.k_num_witness_vars, }, &mut prover_state, instances_witnesses.1, @@ -148,9 +151,9 @@ fn warp_test() { hash_chain_warp .verify( WARPVerifierKey { - m: r1cs.m, - n: r1cs.n, - k: r1cs.k, + m_num_constraints: r1cs.m_num_constraints, + n_num_variables: r1cs.n_num_variables, + k_num_witness_vars: r1cs.k_num_witness_vars, }, &mut verifier_state, acc_x.clone(), @@ -177,12 +180,15 @@ fn warp_test_goldilocks() { let hash_chain_size = 10; let mut rng = thread_rng(); let poseidon_config = poseidon::initialize_poseidon_config::(); - let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + let r1cs = HashChainRelation::, CRHGadget<_>>::arithmetize(&( poseidon_config.clone(), hash_chain_size, )) .unwrap(); - let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code_config = ReedSolomonConfig::::default( + r1cs.k_num_witness_vars, + r1cs.k_num_witness_vars.next_power_of_two(), + ); let code = ReedSolomon::new(code_config); let instances_witnesses: (Vec>, Vec>) = (0..l1) @@ -205,13 +211,13 @@ fn warp_test_goldilocks() { }) .unzip(); - let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + let r1cs = HashChainRelation::, CRHGadget<_>>::arithmetize(&( poseidon_config.clone(), hash_chain_size, )) .unwrap(); - let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); + let warp_config = WARPConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); let hash_chain_warp = WARP::, _, Blake3FieldHasher>::new( warp_config.clone(), @@ -230,9 +236,9 @@ fn warp_test_goldilocks() { .prove( WARPProverKey { index: r1cs.clone(), - m: r1cs.m, - n: r1cs.n, - k: r1cs.k, + m_num_constraints: r1cs.m_num_constraints, + n_num_variables: r1cs.n_num_variables, + k_num_witness_vars: r1cs.k_num_witness_vars, }, &mut prover_state, instances_witnesses.1.clone(), @@ -248,7 +254,7 @@ fn warp_test_goldilocks() { let domainsep = spongefish::domain_separator!("test::warp"); // Use 8 (2*l1) for the total accumulation size to test multi-instance accumulation let warp_config = - WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); + WARPConfig::<_, R1CS>::new(l1, 4, s, t, r1cs.config(), code.code_len()); let hash_chain_warp = WARP::, _, Blake3FieldHasher>::new( @@ -263,9 +269,9 @@ fn warp_test_goldilocks() { .prove( WARPProverKey { index: r1cs.clone(), - m: r1cs.m, - n: r1cs.n, - k: r1cs.k, + m_num_constraints: r1cs.m_num_constraints, + n_num_variables: r1cs.n_num_variables, + k_num_witness_vars: r1cs.k_num_witness_vars, }, &mut prover_state, instances_witnesses.1, @@ -284,9 +290,9 @@ fn warp_test_goldilocks() { hash_chain_warp .verify( WARPVerifierKey { - m: r1cs.m, - n: r1cs.n, - k: r1cs.k, + m_num_constraints: r1cs.m_num_constraints, + n_num_variables: r1cs.n_num_variables, + k_num_witness_vars: r1cs.k_num_witness_vars, }, &mut verifier_state, acc_x.clone(), diff --git a/tests/profile_json.rs b/tests/profile_json.rs index c261917..621bbdb 100644 --- a/tests/profile_json.rs +++ b/tests/profile_json.rs @@ -25,7 +25,7 @@ use warp::relations::{ hashchain::{compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness}, R1CS, }, - BundledPESAT, Relation, ToPolySystem, + Arithmetize, PolyPredicate, Relation, }; use warp::utils::poseidon; use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProverKey}; @@ -61,12 +61,15 @@ fn json_layer_emits_phase_records() { let hash_chain_size = 10; let mut rng = thread_rng(); let poseidon_config = poseidon::initialize_poseidon_config::(); - let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + let r1cs = HashChainRelation::, CRHGadget<_>>::arithmetize(&( poseidon_config.clone(), hash_chain_size, )) .unwrap(); - let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code_config = ReedSolomonConfig::::default( + r1cs.k_num_witness_vars, + r1cs.k_num_witness_vars.next_power_of_two(), + ); let code = ReedSolomon::new(code_config.clone()); let (instances, witnesses): (Vec<_>, Vec<_>) = (0..l1) @@ -89,7 +92,7 @@ fn json_layer_emits_phase_records() { }) .unzip(); - let warp_config = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); + let warp_config = WARPConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); let hash_chain_warp = WARP::, _, Blake3FieldHasher>::new( warp_config, code, @@ -104,9 +107,9 @@ fn json_layer_emits_phase_records() { .prove( WARPProverKey { index: r1cs.clone(), - m: r1cs.m, - n: r1cs.n, - k: r1cs.k, + m_num_constraints: r1cs.m_num_constraints, + n_num_variables: r1cs.n_num_variables, + k_num_witness_vars: r1cs.k_num_witness_vars, }, &mut prover_state, witnesses, diff --git a/tests/verifier_negative.rs b/tests/verifier_negative.rs index ce7f8fc..8a7971d 100644 --- a/tests/verifier_negative.rs +++ b/tests/verifier_negative.rs @@ -37,7 +37,7 @@ use warp::relations::{ hashchain::{compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness}, R1CS, }, - BundledPESAT, Relation, ToPolySystem, + Arithmetize, PolyPredicate, Relation, }; use warp::utils::poseidon; use warp::warp::{ @@ -83,12 +83,15 @@ fn make_fixture() -> Fixture { let hash_chain_size = 10; let mut rng = thread_rng(); let poseidon_config = poseidon::initialize_poseidon_config::(); - let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + let r1cs = HashChainRelation::, CRHGadget<_>>::arithmetize(&( poseidon_config.clone(), hash_chain_size, )) .unwrap(); - let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code_config = ReedSolomonConfig::::default( + r1cs.k_num_witness_vars, + r1cs.k_num_witness_vars.next_power_of_two(), + ); let code = ReedSolomon::new(code_config); let (instances, witnesses): (Vec<_>, Vec<_>) = (0..l1) @@ -113,7 +116,7 @@ fn make_fixture() -> Fixture { // Phase 1: produce `l1` single-round acc states so we have a non-trivial // accumulator to feed phase 2 (l2 > 0 so NumL2Instances is reachable). - let warp_cfg1 = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); + let warp_cfg1 = WARPConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); let w1 = WARP::, _, H>::new( warp_cfg1, code.clone(), @@ -131,9 +134,9 @@ fn make_fixture() -> Fixture { .prove( WARPProverKey { index: r1cs.clone(), - m: r1cs.m, - n: r1cs.n, - k: r1cs.k, + m_num_constraints: r1cs.m_num_constraints, + n_num_variables: r1cs.n_num_variables, + k_num_witness_vars: r1cs.k_num_witness_vars, }, &mut ps, witnesses.clone(), @@ -147,7 +150,7 @@ fn make_fixture() -> Fixture { } // Phase 2: the "real" prove with l2 > 0 accumulated instances. - let warp_cfg2 = WARPConfig::<_, R1CS>::new(8, l1, s, t, r1cs.config(), code.code_len()); + let warp_cfg2 = WARPConfig::<_, R1CS>::new(l1, 4, s, t, r1cs.config(), code.code_len()); let warp = WARP::, _, H>::new(warp_cfg2, code, r1cs.clone(), Blake3FieldHasher::::new()); @@ -157,9 +160,9 @@ fn make_fixture() -> Fixture { .prove( WARPProverKey { index: r1cs.clone(), - m: r1cs.m, - n: r1cs.n, - k: r1cs.k, + m_num_constraints: r1cs.m_num_constraints, + n_num_variables: r1cs.n_num_variables, + k_num_witness_vars: r1cs.k_num_witness_vars, }, &mut ps, witnesses, @@ -172,9 +175,9 @@ fn make_fixture() -> Fixture { Fixture { warp, vk: WARPVerifierKey { - m: r1cs.m, - n: r1cs.n, - k: r1cs.k, + m_num_constraints: r1cs.m_num_constraints, + n_num_variables: r1cs.n_num_variables, + k_num_witness_vars: r1cs.k_num_witness_vars, }, acc_x, proof, @@ -204,7 +207,7 @@ fn happy_path_verifies() { fn tampered_alpha_raises_code_evaluation_point() { let fix = make_fixture(); let mut acc_x = fix.acc_x.clone(); - acc_x.alpha[0][0] += F::from(1u64); + acc_x.alpha_fold_vectors[0][0] += F::from(1u64); assert_err(fix.verify(acc_x, fix.proof.clone()), "CodeEvaluationPoint"); } @@ -212,7 +215,7 @@ fn tampered_alpha_raises_code_evaluation_point() { fn tampered_beta_tau_raises_circuit_evaluation_point() { let fix = make_fixture(); let mut acc_x = fix.acc_x.clone(); - acc_x.beta.0[0][0] += F::from(1u64); + acc_x.beta_twin_pairs.0[0][0] += F::from(1u64); assert_err( fix.verify(acc_x, fix.proof.clone()), "CircuitEvaluationPoint", @@ -223,7 +226,7 @@ fn tampered_beta_tau_raises_circuit_evaluation_point() { fn tampered_beta_x_raises_circuit_evaluation_point() { let fix = make_fixture(); let mut acc_x = fix.acc_x.clone(); - acc_x.beta.1[0][0] += F::from(1u64); + acc_x.beta_twin_pairs.1[0][0] += F::from(1u64); assert_err( fix.verify(acc_x, fix.proof.clone()), "CircuitEvaluationPoint", @@ -261,7 +264,7 @@ fn truncated_auth_j_raises_num_l2_instances() { fn tampered_mu_raises_target() { let fix = make_fixture(); let mut acc_x = fix.acc_x.clone(); - acc_x.mu[0] += F::from(1u64); + acc_x.mu_claimed_evals[0] += F::from(1u64); assert_err(fix.verify(acc_x, fix.proof.clone()), "Target"); } @@ -275,12 +278,15 @@ fn prove_rejects_mismatched_instance_witness_lengths() { let hash_chain_size = 4; let mut rng = thread_rng(); let poseidon_config = poseidon::initialize_poseidon_config::(); - let r1cs = HashChainRelation::, CRHGadget<_>>::into_r1cs(&( + let r1cs = HashChainRelation::, CRHGadget<_>>::arithmetize(&( poseidon_config.clone(), hash_chain_size, )) .unwrap(); - let code_config = ReedSolomonConfig::::default(r1cs.k, r1cs.k.next_power_of_two()); + let code_config = ReedSolomonConfig::::default( + r1cs.k_num_witness_vars, + r1cs.k_num_witness_vars.next_power_of_two(), + ); let code = ReedSolomon::new(code_config); let (instances, witnesses): (Vec<_>, Vec<_>) = (0..l1) @@ -303,7 +309,7 @@ fn prove_rejects_mismatched_instance_witness_lengths() { }) .unzip(); - let warp_cfg = WARPConfig::new(l1, l1, s, t, r1cs.config(), code.code_len()); + let warp_cfg = WARPConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); let warp = WARP::, _, H>::new(warp_cfg, code, r1cs.clone(), Blake3FieldHasher::::new()); @@ -315,9 +321,9 @@ fn prove_rejects_mismatched_instance_witness_lengths() { let result = warp.prove( WARPProverKey { index: r1cs.clone(), - m: r1cs.m, - n: r1cs.n, - k: r1cs.k, + m_num_constraints: r1cs.m_num_constraints, + n_num_variables: r1cs.n_num_variables, + k_num_witness_vars: r1cs.k_num_witness_vars, }, &mut ps, witnesses_short, From b20e7613f03a9cb1e64a47d60ad32c0a056ed615 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 12 May 2026 19:09:33 +0200 Subject: [PATCH 63/68] further cleanup --- src/bin/warp-params.rs | 15 +++++- src/config/mod.rs | 21 ++++++++ src/error.rs | 14 ++++++ src/params/mod.rs | 2 +- src/params/types.rs | 18 ++++++- src/params/validate.rs | 39 +++++++++++++++ src/protocol/ior.rs | 27 ++++++++++ src/protocol/iors/batching.rs | 26 ++++++++-- src/protocol/iors/twin_constraint.rs | 74 +++++++++++++++++++++------- src/protocol/transcript/prover.rs | 13 +++-- src/protocol/transcript/verifier.rs | 8 ++- src/relations/r1cs/mod.rs | 26 +++++++--- src/serialize.rs | 16 +++--- src/warp/accumulator.rs | 16 ++++-- src/warp/decide.rs | 6 +-- src/warp/prove.rs | 41 +++++++++++++-- src/warp/verify.rs | 39 +++++++++++++-- tests/verifier_negative.rs | 4 +- 18 files changed, 339 insertions(+), 66 deletions(-) diff --git a/src/bin/warp-params.rs b/src/bin/warp-params.rs index 5aa0588..b305df8 100644 --- a/src/bin/warp-params.rs +++ b/src/bin/warp-params.rs @@ -3,7 +3,7 @@ use std::process::ExitCode; -use warp::params::{lookup, select, validate, ParamError, Params, Regime, SecurityLevel, PRESETS}; +use warp::params::{inspect, lookup, select, ParamError, Params, Regime, SecurityLevel, PRESETS}; fn main() -> ExitCode { let args: Vec = std::env::args().skip(1).collect(); @@ -114,7 +114,7 @@ fn cmd_validate(args: &[String]) -> ExitCode { } }; let params = Params { s, t }; - match validate(¶ms, field_bits, rate.as_f64(), regime, lambda) { + match inspect(¶ms, field_bits, rate.as_f64(), regime, lambda) { Ok(bound) => { println!( "proximity_bits={:.2} field_admissible={} ood_admissible={} meets_target={}", @@ -245,5 +245,16 @@ fn format_err(e: ParamError) -> String { lambda + warp::params::select::FIELD_EPSILON ) } + ParamError::OodSamplesTooFew { s, min } => { + format!("OOD samples s = {s} is below minimum {min}") + } + ParamError::ProximitySoundnessBelowTarget { + proximity_bits, + target_bits, + } => { + format!( + "proximity soundness {proximity_bits:.2} bits is below target {target_bits} bits" + ) + } } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 555b198..59ef2d1 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,5 +1,6 @@ use ark_ff::Field; +use crate::params::{validate, ParamError, Params, Regime, SecurityLevel, SoundnessBound}; use crate::relations::PolyPredicate; #[derive(Clone)] @@ -35,4 +36,24 @@ impl> WARPConfig { pub fn l_total_fold_factor(&self) -> usize { self.l1_first_fold_factor + self.l2_second_fold_factor } + + /// Fail-closed soundness check on `(s, t)`. Returns `Ok(bound)` only when + /// every admissibility flag passes and proximity soundness meets the + /// target. `WARPConfig::new` itself does not call this (it cannot, the + /// security target / field-size / rate / regime live outside the config); + /// callers building a config for production use **must** call this before + /// invoking the prover. See `docs/paper-mods/mod4_parameter_selection.tex`. + pub fn validate_security( + &self, + field_bits: u32, + code_rate: f64, + regime: Regime, + target: SecurityLevel, + ) -> Result { + let params = Params { + s: self.s_num_ood_samples, + t: self.t_num_queries, + }; + validate(¶ms, field_bits, code_rate, regime, target) + } } diff --git a/src/error.rs b/src/error.rs index f504b67..2c28004 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,6 +17,8 @@ pub enum WARPError { ZeroEvaderSize(usize, usize), #[error("LC does not exist")] R1CSNonExistingLC, + #[error("R1CS construction: {reason}")] + R1CSConstruction { reason: &'static str }, } #[derive(Error, Debug)] @@ -35,6 +37,12 @@ pub enum ProverError { ConfigParameterInvalid { reason: String }, #[error("instance length mismatch: expected {expected}, got {got}")] InstanceLengthMismatch { expected: usize, got: usize }, + #[error("statement layout mismatch: {what} expected {expected}, got {got}")] + StatementShape { + what: &'static str, + expected: usize, + got: usize, + }, } impl From for ProverError { @@ -67,6 +75,12 @@ pub enum VerifierError { SumcheckRound, #[error("Incorrect target")] Target, + #[error("statement layout mismatch: {what} expected {expected}, got {got}")] + StatementShape { + what: &'static str, + expected: usize, + got: usize, + }, } impl From for VerifierError { diff --git a/src/params/mod.rs b/src/params/mod.rs index 75e71f7..8fc1ded 100644 --- a/src/params/mod.rs +++ b/src/params/mod.rs @@ -9,7 +9,7 @@ pub mod validate; pub use presets::{lookup, Preset, PRESETS}; pub use select::select; pub use types::{ParamError, Params, Regime, SecurityLevel, SoundnessBound}; -pub use validate::validate; +pub use validate::{inspect, validate}; #[cfg(test)] mod tests { diff --git a/src/params/types.rs b/src/params/types.rs index f2b335e..fe5c730 100644 --- a/src/params/types.rs +++ b/src/params/types.rs @@ -41,8 +41,22 @@ impl SoundnessBound { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum ParamError { InvalidRate, - FieldTooSmall { field_bits: u32, lambda: u32 }, + FieldTooSmall { + field_bits: u32, + lambda: u32, + }, + /// Returned by `validate()` when `params.s` is below the OOD-samples + /// minimum required for the chosen target. + OodSamplesTooFew { + s: usize, + min: usize, + }, + /// Returned by `validate()` when proximity soundness is below target. + ProximitySoundnessBelowTarget { + proximity_bits: f64, + target_bits: u32, + }, } diff --git a/src/params/validate.rs b/src/params/validate.rs index ab0455d..7305746 100644 --- a/src/params/validate.rs +++ b/src/params/validate.rs @@ -3,12 +3,50 @@ use super::types::{ParamError, Params, Regime, SecurityLevel, SoundnessBound}; const S_MIN: usize = 8; +/// Fail-closed soundness check. Returns `Ok(bound)` only when every +/// admissibility flag passes and proximity soundness meets the target. +/// +/// For diagnostic output that returns the bound regardless of pass/fail, +/// use [`inspect`]. pub fn validate( params: &Params, field_bits: u32, code_rate: f64, regime: Regime, target: SecurityLevel, +) -> Result { + let bound = inspect(params, field_bits, code_rate, regime, target)?; + if !bound.field_admissible { + return Err(ParamError::FieldTooSmall { + field_bits, + lambda: target.bits(), + }); + } + if !bound.ood_admissible { + return Err(ParamError::OodSamplesTooFew { + s: params.s, + min: S_MIN, + }); + } + if bound.proximity_bits < target.bits() as f64 { + return Err(ParamError::ProximitySoundnessBelowTarget { + proximity_bits: bound.proximity_bits, + target_bits: target.bits(), + }); + } + Ok(bound) +} + +/// Diagnostic / non-failing variant of [`validate`]. Returns the full +/// [`SoundnessBound`] even when admissibility checks fail (only structural +/// errors like an invalid rate produce `Err`). Intended for CLI / debug +/// output; security-relevant callers must use [`validate`]. +pub fn inspect( + params: &Params, + field_bits: u32, + code_rate: f64, + regime: Regime, + target: SecurityLevel, ) -> Result { if !(0.0 < code_rate && code_rate < 1.0) { return Err(ParamError::InvalidRate); @@ -23,6 +61,7 @@ pub fn validate( let field_admissible = field_bits >= target.bits() + FIELD_EPSILON; let ood_admissible = params.s >= S_MIN; + let _ = target; Ok(SoundnessBound { proximity_bits, field_admissible, diff --git a/src/protocol/ior.rs b/src/protocol/ior.rs index 1dcd9f8..4ef3683 100644 --- a/src/protocol/ior.rs +++ b/src/protocol/ior.rs @@ -94,6 +94,33 @@ pub trait IOR { } } +/// Single-shot protocol-map domain separator: absorbs the ordered sequence +/// of FS-affecting IOR names into the transcript before any messages or +/// challenges are exchanged. Pairs identically on prover and verifier. +/// +/// Per-IOR labels would be the more obvious design, but WARP intentionally +/// orders FS-transparent IORs (Proximity) differently on the prover vs the +/// verifier; absorbing labels mid-stream would diverge the two transcripts. +/// Committing the full sequence up-front catches permutations of the +/// FS-affecting IORs without disturbing the FS-transparent ones. +pub fn absorb_protocol_map_prover(prover_state: &mut ProverState, names: &[&'static str]) { + prover_state.public_message(b"WARP.protocol_map"); + for name in names { + prover_state.public_message(name.as_bytes()); + prover_state.public_message(b"|"); + } +} + +/// Verifier-side pair of [`absorb_protocol_map_prover`]. Must be called with +/// the identical `names` slice on the same transcript position. +pub fn absorb_protocol_map_verifier(verifier_state: &mut VerifierState, names: &[&'static str]) { + verifier_state.public_message(b"WARP.protocol_map"); + for name in names { + verifier_state.public_message(name.as_bytes()); + verifier_state.public_message(b"|"); + } +} + /// Destructuring carrier for [`IOR::prove`]. pub struct IorProveResult { pub reduced: R, diff --git a/src/protocol/iors/batching.rs b/src/protocol/iors/batching.rs index edfff05..90d9a50 100644 --- a/src/protocol/iors/batching.rs +++ b/src/protocol/iors/batching.rs @@ -13,7 +13,7 @@ use std::collections::HashMap; use std::marker::PhantomData; use crate::count_ops; -use crate::error::VerifierError; +use crate::error::{ProverError, VerifierError}; use crate::protocol::ior::{ProverTriple, IOR}; use crate::protocol::oracles::evaluation::Oracle; use crate::protocol::transcript::EffscVerifierTranscript; @@ -179,7 +179,13 @@ where let n = inputs.oracle.len(); let r = 1 + statement.s_num_ood_samples + statement.t_num_queries; let log_r = log2(r) as usize; - debug_assert_eq!(statement.zetas_prefix.len(), r); + if statement.zetas_prefix.len() != r { + return Err(ProverError::StatementShape { + what: "zetas_prefix", + expected: r, + got: statement.zetas_prefix.len(), + }); + } let xis = prover_state.verifier_messages_vec::(log_r); @@ -250,8 +256,20 @@ where { let r = 1 + statement.s_num_ood_samples + statement.t_num_queries; let log_r = log2(r) as usize; - debug_assert_eq!(statement.zetas_prefix.len(), r); - debug_assert_eq!(inputs.nus_claimed_evals.len(), r); + if statement.zetas_prefix.len() != r { + return Err(VerifierError::StatementShape { + what: "zetas_prefix", + expected: r, + got: statement.zetas_prefix.len(), + }); + } + if inputs.nus_claimed_evals.len() != r { + return Err(VerifierError::StatementShape { + what: "nus_claimed_evals", + expected: r, + got: inputs.nus_claimed_evals.len(), + }); + } // Squeeze ξ matching the prover. let xis: Vec = (0..log_r) diff --git a/src/protocol/iors/twin_constraint.rs b/src/protocol/iors/twin_constraint.rs index c9becda..9554444 100644 --- a/src/protocol/iors/twin_constraint.rs +++ b/src/protocol/iors/twin_constraint.rs @@ -18,7 +18,7 @@ use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState use std::marker::PhantomData; use crate::count_ops; -use crate::error::VerifierError; +use crate::error::{ProverError, VerifierError}; use crate::protocol::ior::{ProverTriple, IOR}; use crate::protocol::oracles::evaluation::Oracle; use crate::protocol::transcript::EffscVerifierTranscript; @@ -175,19 +175,45 @@ pub struct TwinConstraintProverInputs<'a, F: Field> { /// Deferred oracle check: `final_claim ≟ eq(τ,γ)·(ν₀ + ω·η)`. Cannot fire /// inside `verify` because ν₀, η arrive on the transcript only after Bridge. +/// +/// **Invariant:** every instance must be discharged before the verifier +/// returns. Fields are private (only constructable inside `TwinConstraint`) +/// and `#[must_use]` makes drop-without-use a compile warning. The +/// orchestrator can additionally assert `is_discharged()` after `verify` to +/// catch any composition that forgets the call. +#[must_use = "DeferredOracleCheck must be discharged by the downstream IOR; \ + dropping it without calling discharge() leaves the verifier unsound"] pub struct DeferredOracleCheck { - pub omega_zero_check_randomness: F, - pub tau_zero_check_challenges: Vec, - pub claim: F, + omega_zero_check_randomness: F, + tau_zero_check_challenges: Vec, + claim: F, + discharged: std::cell::Cell, } impl DeferredOracleCheck { + pub(crate) fn new(omega: F, tau: Vec, claim: F) -> Self { + Self { + omega_zero_check_randomness: omega, + tau_zero_check_challenges: tau, + claim, + discharged: std::cell::Cell::new(false), + } + } + pub fn discharge(&self, gamma: &[F], nu_0: F, eta: F) -> Result<(), VerifierError> { let expected = eq_poly_non_binary(&self.tau_zero_check_challenges, gamma) * (nu_0 + self.omega_zero_check_randomness * eta); - (expected == self.claim) - .then_some(()) - .ok_or(VerifierError::Target) + let ok = expected == self.claim; + self.discharged.set(true); + ok.then_some(()).ok_or(VerifierError::Target) + } + + /// Returns true once `discharge` has been invoked at least once on this + /// handle. The orchestrator should assert this is true after `verify` + /// returns; an undischarged handle means the protocol composition has a + /// soundness gap. + pub fn is_discharged(&self) -> bool { + self.discharged.get() } } @@ -276,9 +302,8 @@ where let beta_taus: Vec> = statement .acc_instance .beta_twin_pairs - .0 .iter() - .cloned() + .map(|p| p.tau.clone()) .chain(statement.l1_taus_zero_check_challenges.iter().cloned()) .collect(); let beta_tau = scale_and_sum(&beta_taus, &gamma_eq_evals); @@ -287,11 +312,11 @@ where gamma_sumcheck_challenges: inputs.gamma_sumcheck_challenges.clone(), zeta_0, beta_tau, - deferred: DeferredOracleCheck { - omega_zero_check_randomness: inputs.omega_zero_check_randomness, - tau_zero_check_challenges: inputs.tau_zero_check_challenges.clone(), - claim: inputs.final_claim, - }, + deferred: DeferredOracleCheck::new( + inputs.omega_zero_check_randomness, + inputs.tau_zero_check_challenges.clone(), + inputs.final_claim, + ), } } @@ -334,8 +359,8 @@ where let z_vecs: Vec> = statement .acc_instance .beta_twin_pairs - .1 .iter() + .map(|p| &p.x) .zip(witness.acc_witness_w) .chain(witness.instances.iter().zip(witness.witnesses)) .map(|(x, w)| concat_slices(x, w)) @@ -345,9 +370,8 @@ where let beta_vecs: Vec> = statement .acc_instance .beta_twin_pairs - .0 .iter() - .cloned() + .map(|p| p.tau.clone()) .chain(statement.l1_taus_zero_check_challenges.iter().cloned()) .collect(); @@ -373,14 +397,26 @@ where count_ops!(TwinConstraintRounds, log_l as u64); sumcheck(&mut cc, log_l, prover_state, noop_hook) }; - debug_assert_eq!(proof.challenges.len(), log_l); + if proof.challenges.len() != log_l { + return Err(ProverError::StatementShape { + what: "sumcheck.challenges", + expected: log_l, + got: proof.challenges.len(), + }); + } // d. pull only the reduced *witness* halves out of CC. ζ₀ and β_τ // are NOT pulled here — `reduce_statement` recomputes them from // (statement, γ) via `scale_and_sum`, which is the single source // of truth that both prover and verifier go through. let reduced = cc.tablewise(); - debug_assert!(reduced.iter().all(|t| t.len() == 1)); + if !reduced.iter().all(|t| t.len() == 1) { + return Err(ProverError::StatementShape { + what: "sumcheck.tablewise (singleton after full fold)", + expected: 1, + got: reduced.iter().map(|t| t.len()).max().unwrap_or(0), + }); + } let f = reduced[0][0].clone(); let z = reduced[1][0].clone(); diff --git a/src/protocol/transcript/prover.rs b/src/protocol/transcript/prover.rs index b7109e2..bfc4d30 100644 --- a/src/protocol/transcript/prover.rs +++ b/src/protocol/transcript/prover.rs @@ -38,14 +38,17 @@ where prover_state.prover_message(f); } - for tau in &self.beta_twin_pairs.0 { - for f in tau { + // Layout: all τ vectors first (l2 of them), then all x vectors. + // Verifier reads in the same order. Keeping the τ/x halves in + // separate passes lets the verifier reconstruct the pair list + // without needing length-prefixes per pair. + for pair in &self.beta_twin_pairs { + for f in &pair.tau { prover_state.prover_message(f); } } - - for x in &self.beta_twin_pairs.1 { - for f in x { + for pair in &self.beta_twin_pairs { + for f in &pair.x { prover_state.prover_message(f); } } diff --git a/src/protocol/transcript/verifier.rs b/src/protocol/transcript/verifier.rs index 201b5f0..ba28561 100644 --- a/src/protocol/transcript/verifier.rs +++ b/src/protocol/transcript/verifier.rs @@ -63,13 +63,19 @@ where .map(|_| verifier_state.prover_messages_vec(instance_len)) .collect::>()?; + let beta_twin_pairs: Vec> = taus + .into_iter() + .zip(xs) + .map(|(tau, x)| crate::warp::accumulator::BetaTwinPair { tau, x }) + .collect(); + let eta: Vec = verifier_state.prover_messages_vec(l2)?; Ok(Self { rt_merkle_roots: rt, alpha_fold_vectors: alpha, mu_claimed_evals: mu, - beta_twin_pairs: (taus, xs), + beta_twin_pairs, eta_predicate_evals: eta, }) } diff --git a/src/relations/r1cs/mod.rs b/src/relations/r1cs/mod.rs index 3c0db85..48cf76a 100644 --- a/src/relations/r1cs/mod.rs +++ b/src/relations/r1cs/mod.rs @@ -30,11 +30,18 @@ impl TryFrom> for R1CS { fn try_from(cs: ConstraintSystemRef) -> Result { use ark_relations::gr1cs::R1CS_PREDICATE_LABEL; - let inner = cs.into_inner().unwrap(); - let all_matrices = inner.to_matrices().unwrap(); - let r1cs_matrices = all_matrices - .get(R1CS_PREDICATE_LABEL) - .expect("R1CS predicate must exist"); + let inner = cs.into_inner().ok_or(WARPError::R1CSConstruction { + reason: "constraint system has outstanding borrows", + })?; + let all_matrices = inner.to_matrices().map_err(|_| WARPError::R1CSConstruction { + reason: "constraint system not finalized or matrices unavailable", + })?; + let r1cs_matrices = + all_matrices + .get(R1CS_PREDICATE_LABEL) + .ok_or(WARPError::R1CSConstruction { + reason: "R1CS predicate not present in constraint system", + })?; let num_constraints = inner .get_predicate_num_constraints(R1CS_PREDICATE_LABEL) @@ -44,9 +51,14 @@ impl TryFrom> for R1CS { let m = num_constraints.next_power_of_two(); let n = inner.num_instance_variables() + inner.num_witness_variables(); let k = inner.num_witness_variables(); + if n == 0 { + return Err(WARPError::R1CSConstruction { + reason: "n = num_instance_variables + num_witness_variables must be > 0", + }); + } - // both `unwrap()` calls below are safe since warp/lib.rs forbids compiling on platforms - // with 16-bits pointers width + // Safe: `m` is always ≥ 1 (next_power_of_two of any usize is ≥ 1) and `n > 0` + // checked above. usize→u32 cast is safe on ≥32-bit platforms per lib.rs. let log_m = m.ilog2().try_into().unwrap(); let log_n = n.ilog2().try_into().unwrap(); diff --git a/src/serialize.rs b/src/serialize.rs index b160c3d..6836b0b 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -29,23 +29,23 @@ where .serialize_with_mode(&mut writer, compress)?; self.mu_claimed_evals .serialize_with_mode(&mut writer, compress)?; - self.beta_twin_pairs - .0 - .serialize_with_mode(&mut writer, compress)?; - self.beta_twin_pairs - .1 - .serialize_with_mode(&mut writer, compress)?; + let taus: Vec<&Vec> = self.beta_twin_pairs.iter().map(|p| &p.tau).collect(); + let xs: Vec<&Vec> = self.beta_twin_pairs.iter().map(|p| &p.x).collect(); + taus.serialize_with_mode(&mut writer, compress)?; + xs.serialize_with_mode(&mut writer, compress)?; self.eta_predicate_evals .serialize_with_mode(&mut writer, compress)?; Ok(()) } fn serialized_size(&self, compress: Compress) -> usize { + let taus: Vec<&Vec> = self.beta_twin_pairs.iter().map(|p| &p.tau).collect(); + let xs: Vec<&Vec> = self.beta_twin_pairs.iter().map(|p| &p.x).collect(); self.rt_merkle_roots.serialized_size(compress) + self.alpha_fold_vectors.serialized_size(compress) + self.mu_claimed_evals.serialized_size(compress) - + self.beta_twin_pairs.0.serialized_size(compress) - + self.beta_twin_pairs.1.serialized_size(compress) + + taus.serialized_size(compress) + + xs.serialized_size(compress) + self.eta_predicate_evals.serialized_size(compress) } } diff --git a/src/warp/accumulator.rs b/src/warp/accumulator.rs index 3dc503b..69ce2d2 100644 --- a/src/warp/accumulator.rs +++ b/src/warp/accumulator.rs @@ -3,13 +3,22 @@ use ark_mt::MerkleHasher; use crate::crypto::merkle::WarpCommitted; +/// Per-instance β coordinates absorbed into the accumulator: the `(τ, x)` +/// twin pair. Replaces the old parallel-`Vec` tuple shape which let callers +/// accidentally swap or desync the two halves. +#[derive(Clone, Debug)] +pub struct BetaTwinPair { + pub tau: Vec, + pub x: Vec, +} + /// Public part of an accumulated claim: `(rt, α, μ, (τ, x), η)` in the paper. #[derive(Clone)] pub struct AccumulatorInstance { pub rt_merkle_roots: Vec, pub alpha_fold_vectors: Vec>, pub mu_claimed_evals: Vec, - pub beta_twin_pairs: (Vec>, Vec>), + pub beta_twin_pairs: Vec>, pub eta_predicate_evals: Vec, } @@ -19,7 +28,7 @@ impl AccumulatorInstance { rt_merkle_roots: vec![], alpha_fold_vectors: vec![], mu_claimed_evals: vec![], - beta_twin_pairs: (vec![], vec![]), + beta_twin_pairs: vec![], eta_predicate_evals: vec![], } } @@ -28,8 +37,7 @@ impl AccumulatorInstance { self.rt_merkle_roots.extend(other.rt_merkle_roots); self.alpha_fold_vectors.extend(other.alpha_fold_vectors); self.mu_claimed_evals.extend(other.mu_claimed_evals); - self.beta_twin_pairs.0.extend(other.beta_twin_pairs.0); - self.beta_twin_pairs.1.extend(other.beta_twin_pairs.1); + self.beta_twin_pairs.extend(other.beta_twin_pairs); self.eta_predicate_evals.extend(other.eta_predicate_evals); self } diff --git a/src/warp/decide.rs b/src/warp/decide.rs index 33526e8..31794cf 100644 --- a/src/warp/decide.rs +++ b/src/warp/decide.rs @@ -49,15 +49,15 @@ where .then_some(()) .ok_or(DeciderError::MLExtensionEvaluation)?; - let tau = &acc_instance.beta_twin_pairs.0[0]; + let tau = &acc_instance.beta_twin_pairs[0].tau; let tau_zero_evader = compute_hypercube_eq_evals(tau.len(), tau); - let mut z = acc_instance.beta_twin_pairs.1[0].clone(); + let mut z = acc_instance.beta_twin_pairs[0].x.clone(); z.extend(acc_witness.w_witnesses[0].clone()); let computed_eta = self .params .predicate .evaluate_bundled(&tau_zero_evader, &z) - .unwrap(); + .map_err(|_| DeciderError::BundledEvaluation)?; (computed_eta == acc_instance.eta_predicate_evals[0]) .then_some(()) .ok_or(DeciderError::BundledEvaluation)?; diff --git a/src/warp/prove.rs b/src/warp/prove.rs index 68735d5..6f3d18e 100644 --- a/src/warp/prove.rs +++ b/src/warp/prove.rs @@ -6,7 +6,7 @@ use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState use std::marker::PhantomData; use crate::error::ProverError; -use crate::protocol::ior::IorProveResult; +use crate::protocol::ior::{absorb_protocol_map_prover, IorProveResult, IOR}; use crate::protocol::iors::{ batching::{ Batching, BatchingProverInputs, BatchingReducedStatement, BatchingReducedWitness, @@ -28,7 +28,7 @@ use crate::protocol::iors::{ use crate::protocol::transcript::absorb_instances; use crate::prove_ior; use crate::relations::PolyPredicate; -use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; +use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness, BetaTwinPair}; use crate::warp::keys::WARPProverKey; use crate::warp::proof::{ProveResult, WARPProof}; use crate::warp::scheme::WARP; @@ -74,6 +74,22 @@ where ), }); } + if pk.m_num_constraints == 0 || pk.n_num_variables == 0 { + return Err(ProverError::ConfigParameterInvalid { + reason: format!( + "pk.m_num_constraints = {} and pk.n_num_variables = {} must both be > 0", + pk.m_num_constraints, pk.n_num_variables + ), + }); + } + if pk.n_num_variables < pk.k_num_witness_vars { + return Err(ProverError::ConfigParameterInvalid { + reason: format!( + "pk.n_num_variables ({}) < pk.k_num_witness_vars ({})", + pk.n_num_variables, pk.k_num_witness_vars + ), + }); + } let expected_instance_len = pk.n_num_variables - pk.k_num_witness_vars; if instances[0].len() != expected_instance_len { return Err(ProverError::InstanceLengthMismatch { @@ -102,6 +118,22 @@ where let log_n = log2(n_code_len) as usize; let n_minus_k = pk.n_num_variables - pk.k_num_witness_vars; + // Domain-separation for the FS-affecting IOR sequence. Order here + // must match `WARP::verify`. (Proximity is FS-transparent and + // intentionally omitted; its position is allowed to differ between + // prover and verifier without affecting soundness.) + absorb_protocol_map_prover( + prover_state, + &[ + Pesat::::NAME, + TwinConstraint::::NAME, + Bridge::::NAME, + Ood::::NAME, + SampleQueries::::NAME, + Batching::::NAME, + ], + ); + absorb_instances(prover_state, &instances); acc_instance.absorb_into(prover_state); @@ -306,7 +338,10 @@ where rt_merkle_roots: vec![td_new.root().clone()], alpha_fold_vectors: vec![alpha_sumcheck_challenges], mu_claimed_evals: vec![mu_claimed_eval], - beta_twin_pairs: (vec![beta_tau], vec![new_x]), + beta_twin_pairs: vec![BetaTwinPair { + tau: beta_tau, + x: new_x, + }], eta_predicate_evals: vec![eta_predicate_eval], }; let new_acc_witness = AccumulatorWitness { diff --git a/src/warp/verify.rs b/src/warp/verify.rs index 04a2a9f..329a2df 100644 --- a/src/warp/verify.rs +++ b/src/warp/verify.rs @@ -8,7 +8,7 @@ use std::marker::PhantomData; use crate::crypto::merkle::warp_scheme; use crate::error::VerifierError; -use crate::protocol::ior::IorVerifyResult; +use crate::protocol::ior::{absorb_protocol_map_verifier, IorVerifyResult, IOR}; use crate::protocol::iors::{ batching::{Batching, BatchingReducedStatement, BatchingStatement, BatchingVerifierInputs}, bridge::{Bridge, BridgeReducedStatement, BridgeStatement, BridgeVerifierInputs}, @@ -49,6 +49,20 @@ where let log_n = log2(n_code_len) as usize; let n_minus_k = vk.n_num_variables - vk.k_num_witness_vars; + // Paired with `WARP::prove`. Catches reorder/swap of FS-affecting + // IORs between prover and verifier. + absorb_protocol_map_verifier( + verifier_state, + &[ + Pesat::::NAME, + TwinConstraint::::NAME, + Bridge::::NAME, + Ood::::NAME, + SampleQueries::::NAME, + Batching::::NAME, + ], + ); + let (l1_xs, parsed_acc) = parse_statement::( verifier_state, self.params.config.l1_first_fold_factor, @@ -59,11 +73,19 @@ where )?; let acc_alpha_first = acc_instance.alpha_fold_vectors[0].clone(); - let acc_beta_0_first = acc_instance.beta_twin_pairs.0[0].clone(); - let acc_beta_1_first = acc_instance.beta_twin_pairs.1[0].clone(); + let acc_beta_0_first = acc_instance.beta_twin_pairs[0].tau.clone(); + let acc_beta_1_first = acc_instance.beta_twin_pairs[0].x.clone(); let acc_mu_first = acc_instance.mu_claimed_evals[0]; - let l2_taus = parsed_acc.beta_twin_pairs.0.clone(); - let l2_xs = parsed_acc.beta_twin_pairs.1.clone(); + let l2_taus: Vec> = parsed_acc + .beta_twin_pairs + .iter() + .map(|p| p.tau.clone()) + .collect(); + let l2_xs: Vec> = parsed_acc + .beta_twin_pairs + .iter() + .map(|p| p.x.clone()) + .collect(); let l2_roots = parsed_acc.rt_merkle_roots.clone(); let pesat_ior = Pesat:: { @@ -146,6 +168,13 @@ where }, )?; + // Composition-safety assertion: any downstream IOR consuming TC's + // deferred check must have called discharge(). If this fires the + // protocol composition has a soundness gap. + if !deferred.is_discharged() { + return Err(VerifierError::Target); + } + let IorVerifyResult { reduced: OodReducedStatement { diff --git a/tests/verifier_negative.rs b/tests/verifier_negative.rs index 8a7971d..346256a 100644 --- a/tests/verifier_negative.rs +++ b/tests/verifier_negative.rs @@ -215,7 +215,7 @@ fn tampered_alpha_raises_code_evaluation_point() { fn tampered_beta_tau_raises_circuit_evaluation_point() { let fix = make_fixture(); let mut acc_x = fix.acc_x.clone(); - acc_x.beta_twin_pairs.0[0][0] += F::from(1u64); + acc_x.beta_twin_pairs[0].tau[0] += F::from(1u64); assert_err( fix.verify(acc_x, fix.proof.clone()), "CircuitEvaluationPoint", @@ -226,7 +226,7 @@ fn tampered_beta_tau_raises_circuit_evaluation_point() { fn tampered_beta_x_raises_circuit_evaluation_point() { let fix = make_fixture(); let mut acc_x = fix.acc_x.clone(); - acc_x.beta_twin_pairs.1[0][0] += F::from(1u64); + acc_x.beta_twin_pairs[0].x[0] += F::from(1u64); assert_err( fix.verify(acc_x, fix.proof.clone()), "CircuitEvaluationPoint", From 7814cd7d042c309b85d7e38410fd3f4237b9194c Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 12 May 2026 19:09:54 +0200 Subject: [PATCH 64/68] fmt --- src/relations/r1cs/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/relations/r1cs/mod.rs b/src/relations/r1cs/mod.rs index 48cf76a..2060d96 100644 --- a/src/relations/r1cs/mod.rs +++ b/src/relations/r1cs/mod.rs @@ -33,9 +33,11 @@ impl TryFrom> for R1CS { let inner = cs.into_inner().ok_or(WARPError::R1CSConstruction { reason: "constraint system has outstanding borrows", })?; - let all_matrices = inner.to_matrices().map_err(|_| WARPError::R1CSConstruction { - reason: "constraint system not finalized or matrices unavailable", - })?; + let all_matrices = inner + .to_matrices() + .map_err(|_| WARPError::R1CSConstruction { + reason: "constraint system not finalized or matrices unavailable", + })?; let r1cs_matrices = all_matrices .get(R1CS_PREDICATE_LABEL) From f497bb2f29af43746097abc0b5421035d4276eca Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Wed, 13 May 2026 16:56:37 +0200 Subject: [PATCH 65/68] warp through vc --- Cargo.toml | 3 +- benches/warp_rs.rs | 20 ++-- src/crypto/merkle/mod.rs | 58 +++++----- src/protocol/iors/bridge.rs | 80 +++++++------ src/protocol/iors/pesat.rs | 74 +++++++----- src/protocol/iors/proximity.rs | 150 ++++++++++++++----------- src/protocol/iors/twin_constraint.rs | 26 +++-- src/protocol/oracles/indexed_merkle.rs | 65 ++--------- src/protocol/transcript/prover.rs | 12 +- src/protocol/transcript/verifier.rs | 26 ++--- src/serialize.rs | 54 ++++----- src/warp/accumulator.rs | 57 +++++++--- src/warp/decide.rs | 25 +++-- src/warp/params.rs | 13 ++- src/warp/proof.rs | 36 +++--- src/warp/prove.rs | 67 +++++------ src/warp/scheme.rs | 31 +++-- src/warp/verify.rs | 150 ++++++++++++++----------- tests/integration_warp.rs | 71 ++++++++---- tests/verifier_negative.rs | 73 +++++++----- 20 files changed, 595 insertions(+), 496 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 843bb5f..d6ffae8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ rayon = "1" serde = { version = "1.0.0", features = ["derive"] } serde_json = "1.0" spongefish = { version = "0.7.0", features = ["ark-ff"] } +rand_core = { version = "0.6", default-features = false } effsc = { git = "https://github.com/compsec-epfl/efficient-sumcheck.git" } thiserror = "2.0.16" tracing = { version = "0.1", default-features = false, features = ["std", "attributes"] } @@ -30,7 +31,7 @@ tracing-subscriber = { version = "0.3", default-features = false, features = ["f libc = { version = "0.2", optional = true } ark-codes = { git = "https://github.com/z-tech/ark-codes.git", branch = "z-tech/arkworks-0.6.0" } ark-vc = { git = "https://github.com/arkworks-rs/ark-vc.git", branch = "z-tech/hash-regions" } -ark-mt = { git = "https://github.com/arkworks-rs/ark-vc.git", branch = "z-tech/hash-regions", features = ["blake3", "blake3-field"] } +ark-mt = { git = "https://github.com/arkworks-rs/ark-vc.git", branch = "z-tech/hash-regions", features = ["blake3", "blake3-field", "vc"] } [dev-dependencies] ark-bls12-381 = "0.6.0" diff --git a/benches/warp_rs.rs b/benches/warp_rs.rs index 3cce80d..80b971b 100644 --- a/benches/warp_rs.rs +++ b/benches/warp_rs.rs @@ -2,8 +2,12 @@ use ark_codes::reed_solomon::config::ReedSolomonConfig; use ark_codes::reed_solomon::ReedSolomon; use ark_codes::traits::LinearCode; -use ark_mt::blake3::Blake3FieldHasher; +use ark_mt::{ + blake3::Blake3FieldHasher, hash_region::HashRegion, scheme::MerkleCommitment, + shape::PerfectBinary, +}; use ark_std::rand::thread_rng; +use ark_vc::mvc::MultiVectorCommitment; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use utils::domainsep::init_prover_state; use utils::hash_chain::{get_hashchain_instance_witness_pairs, get_hashchain_r1cs}; @@ -32,15 +36,17 @@ pub fn bench_rs_warp_fields(c: &mut Criterion) { let s = 2; let t = 125; + type V = MerkleCommitment>, PerfectBinary>; + for l in [32, 64, 128, 256, 512] { let warp_config = WARPConfig::new(l, 0, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = WARP::<_, _, _, Blake3FieldHasher>::new( - warp_config.clone(), - code.clone(), - r1cs.clone(), - Blake3FieldHasher::::new(), - ); + let pp = ::setup_multiple(0, code.code_len(), t, &mut rng) + .expect("setup_multiple"); + let (ck, vk) = ::trim_multiple(&pp, 0, code.code_len(), t) + .expect("trim_multiple"); + let hash_chain_warp = + WARP::<_, _, _, V>::new(warp_config.clone(), code.clone(), r1cs.clone(), ck, vk); let instances_witnesses = get_hashchain_instance_witness_pairs(l, &poseidon_config, HASHCHAIN_SIZE, &mut rng); diff --git a/src/crypto/merkle/mod.rs b/src/crypto/merkle/mod.rs index 339aeef..7767d11 100644 --- a/src/crypto/merkle/mod.rs +++ b/src/crypto/merkle/mod.rs @@ -1,41 +1,33 @@ -//! Warp's vector-commitment layer, built on `ark-vc` / `ark-mt`. +//! Vector-commitment plumbing for warp. //! -//! Warp commits to L1 codewords interleaved into one Merkle tree (PESAT) -//! and to single folded codewords across the accumulator. Both shapes -//! are handled by `MultiVectorMerkleCommitment` (the m=1 case is a -//! degenerate single-codeword tree). +//! Protocol code is generic over `ark_vc::mvc::MultiVectorCommitment`. +//! This module's only job is the [`CommittedCodewords`] helper that +//! pairs `(Commitment, CommitmentState)` from the trait with the +//! original column-codewords — the trait's `State` exposes leaves as +//! row-tuples, but warp threads codewords back for proximity opens +//! and the decider's recompute check. -use ark_codes::traits::LinearCode; use ark_ff::Field; -use ark_mt::{ - hash_region::HashRegion, multi_vector::MultiVectorMerkleCommitment, shape::PerfectBinary, - MerkleHasher, -}; +use ark_vc::mvc::MultiVectorCommitment; -/// Warp's commitment scheme: any field-symbol hasher over a -/// power-of-two binary tree. -pub type WarpScheme = MultiVectorMerkleCommitment; - -/// Output of a commit: the codewords + state needed to open at any index. -pub type WarpCommitted = ark_mt::multi_vector::MultiVectorCommitted; - -/// Verifier-side opening (indices + per-codeword opened values). -pub type WarpOpening = ark_mt::multi_vector::MultiVectorOpening; - -/// Authentication paths for the opened indices. -pub type WarpProof = ark_mt::OpeningProof>; +pub struct CommittedCodewords> { + pub commitment: V::Commitment, + pub state: V::CommitmentState, + pub codewords: Vec>, +} -/// Build a `WarpScheme` for a given hasher and codeword length. -pub fn warp_scheme(hasher: H, code_len: usize) -> WarpScheme +impl Clone for CommittedCodewords where - H: MerkleHasher>, - F: Field + Clone, + F: Field, + V: MultiVectorCommitment, + V::Commitment: Clone, + V::CommitmentState: Clone, { - MultiVectorMerkleCommitment::new(hasher, PerfectBinary::with_num_leaves(code_len)) -} - -/// Encode `witnesses` into codewords. Returns the codewords (one per -/// witness). Each codeword has length `code.code_len()`. -pub fn encode_codewords>(code: &C, witnesses: &[Vec]) -> Vec> { - witnesses.iter().map(|w| code.encode(w)).collect() + fn clone(&self) -> Self { + Self { + commitment: self.commitment.clone(), + state: self.state.clone(), + codewords: self.codewords.clone(), + } + } } diff --git a/src/protocol/iors/bridge.rs b/src/protocol/iors/bridge.rs index 707b6e9..12bf430 100644 --- a/src/protocol/iors/bridge.rs +++ b/src/protocol/iors/bridge.rs @@ -1,13 +1,14 @@ //! TC → OOD bridge IOR. Publishes `(td_new, η, ν₀)` and discharges //! TwinConstraint's deferred oracle check `eq(τ,γ)·(ν₀ + ω·η) ≟ final_claim`. use ark_ff::Field; -use ark_mt::MerkleHasher; +use ark_vc::mvc::MultiVectorCommitment; +use ark_vc::vc::VectorCommitment; use effsc::hypercube::compute_hypercube_eq_evals; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; use crate::count_ops; -use crate::crypto::merkle::{warp_scheme, WarpCommitted}; +use crate::crypto::merkle::CommittedCodewords; use crate::error::{ProverError, VerifierError}; use crate::protocol::ior::{ProverTriple, IOR}; use crate::protocol::iors::twin_constraint::DeferredOracleCheck; @@ -26,15 +27,14 @@ pub struct BridgeWitness<'a, F: Field> { pub f_oracle: &'a Oracle, } -pub struct BridgeProverInputs<'a, F, P, H> +pub struct BridgeProverInputs<'a, F, P, V> where F: Field, P: PolyPredicate, - H: MerkleHasher>, + V: MultiVectorCommitment, { pub predicate: &'a P, - pub hasher: &'a H, - pub code_len: usize, + pub ck: &'a V::CommitterKey, pub _f: PhantomData, } @@ -43,42 +43,50 @@ pub struct BridgeVerifierInputs<'a, F: Field> { pub gamma_twin_constraint_challenges: &'a [F], } -pub struct BridgeReductionInputs { +pub struct BridgeReductionInputs +where + F: Field, + V: MultiVectorCommitment, +{ pub eta_predicate_eval: F, pub nu_0_oracle_eval: F, - pub td_new_root: H::Digest, + pub td_new_commitment: V::Commitment, } -pub struct BridgeReducedStatement { +pub struct BridgeReducedStatement +where + F: Field, + V: MultiVectorCommitment, +{ pub eta_predicate_eval: F, pub nu_0_oracle_eval: F, - pub td_new_root: H::Digest, + pub td_new_commitment: V::Commitment, } -pub struct BridgeReducedWitness +pub struct BridgeReducedWitness where F: Field, - H: MerkleHasher>, + V: MultiVectorCommitment, { - pub td_new: WarpCommitted, + pub td_new: CommittedCodewords, pub new_x: Vec, pub new_w: Vec, } -pub struct Bridge(PhantomData, PhantomData

    , PhantomData); +pub struct Bridge(PhantomData, PhantomData

    , PhantomData); -impl Default for Bridge { +impl Default for Bridge { fn default() -> Self { Self(PhantomData, PhantomData, PhantomData) } } -impl IOR for Bridge +impl IOR for Bridge where F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, P: PolyPredicate, - H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize + Clone, + V: MultiVectorCommitment, + V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize + Clone, { const NAME: &'static str = "Bridge"; @@ -91,17 +99,17 @@ where where Self: 'b; type ProverInputs<'b> - = BridgeProverInputs<'b, F, P, H> + = BridgeProverInputs<'b, F, P, V> where Self: 'b; type VerifierInputs<'b> = BridgeVerifierInputs<'b, F> where Self: 'b; - type ReductionInputs = BridgeReductionInputs; - type ReducedStatement = BridgeReducedStatement; + type ReductionInputs = BridgeReductionInputs; + type ReducedStatement = BridgeReducedStatement; type ProofString = (); - type ReducedWitness = BridgeReducedWitness; + type ReducedWitness = BridgeReducedWitness; type VerifierOutputs = (); fn reduce_statement<'a>( @@ -115,7 +123,7 @@ where BridgeReducedStatement { eta_predicate_eval: inputs.eta_predicate_eval, nu_0_oracle_eval: inputs.nu_0_oracle_eval, - td_new_root: inputs.td_new_root.clone(), + td_new_commitment: inputs.td_new_commitment.clone(), } } @@ -149,17 +157,23 @@ where let new_x = new_x_slice.to_vec(); let new_w = new_w_slice.to_vec(); - // td_new ← Merkle.commit(f.evals()) + // td_new ← V.commit(f.evals()) let td_new = { let _s = tracing::info_span!("bridge.commit_new_oracle").entered(); count_ops!(MerkleTreeBuilds); - let scheme = warp_scheme::(inputs.hasher.clone(), inputs.code_len); - scheme.commit(&[witness.f_oracle.evals().to_vec()]) + let codeword = witness.f_oracle.evals().to_vec(); + let (commitment, state) = ::commit(inputs.ck, codeword.iter()) + .map_err(|_| ProverError::SpongeFish)?; + CommittedCodewords:: { + commitment, + state, + codewords: vec![codeword], + } }; - let td_new_root = td_new.root().clone(); + let td_new_commitment: V::Commitment = td_new.commitment.clone(); - // Absorb (td_new.root, η, ν₀) - prover_state.prover_message(&td_new_root); + // Absorb (td_new.commitment, η, ν₀) + prover_state.prover_message(&td_new_commitment); prover_state.prover_message(&eta); prover_state.prover_message(&nu_0); @@ -167,7 +181,7 @@ where BridgeReductionInputs { eta_predicate_eval: eta, nu_0_oracle_eval: nu_0, - td_new_root, + td_new_commitment, }, (), BridgeReducedWitness { @@ -188,8 +202,8 @@ where where Self: 'b, { - // Read (td_digest, η, ν₀) from the transcript. - let td_new_root: H::Digest = verifier_state.prover_message()?; + // Read (commitment, η, ν₀) from the transcript. + let td_new_commitment: V::Commitment = verifier_state.prover_message()?; let eta: F = verifier_state.prover_message()?; let nu_0: F = verifier_state.prover_message()?; @@ -202,7 +216,7 @@ where BridgeReductionInputs { eta_predicate_eval: eta, nu_0_oracle_eval: nu_0, - td_new_root, + td_new_commitment, }, (), )) diff --git a/src/protocol/iors/pesat.rs b/src/protocol/iors/pesat.rs index 0afa3e5..c9235da 100644 --- a/src/protocol/iors/pesat.rs +++ b/src/protocol/iors/pesat.rs @@ -1,17 +1,18 @@ //! PESAT Reduction IOR. //! -//! Encodes fresh witnesses into codewords, commits via a multi-vector -//! Merkle tree (one root over all l1 codewords), absorbs commitment + -//! code evaluations, and derives the τ zero-check challenges. +//! Encodes fresh witnesses into codewords, commits via the trait's +//! joint-commit path (one commitment over all l1 codewords), absorbs +//! that commitment + code evaluations, and derives the τ zero-check +//! challenges. //! use ark_codes::traits::LinearCode; use ark_ff::{Field, PrimeField}; -use ark_mt::MerkleHasher; +use ark_vc::mvc::MultiVectorCommitment; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; use crate::count_ops; -use crate::crypto::merkle::{encode_codewords, warp_scheme, WarpCommitted}; +use crate::crypto::merkle::CommittedCodewords; use crate::error::VerifierError; use crate::protocol::ior::{ProverTriple, IOR}; @@ -34,37 +35,42 @@ pub struct PesatReducedStatement { pub taus_zero_check_challenges: Vec>, } -pub struct PesatReducedWitness +pub struct PesatReducedWitness where F: Field, - H: MerkleHasher>, + V: MultiVectorCommitment, { pub codewords: Vec>, - pub td_0_committed_codeword: WarpCommitted, + pub td_0_committed_codeword: CommittedCodewords, } -pub struct PesatVerifierOutputs { - pub rt_0_fresh_merkle_root: H::Digest, +pub struct PesatVerifierOutputs +where + F: Field, + V: MultiVectorCommitment, +{ + pub rt_0_fresh_commitment: V::Commitment, } -/// PESAT IOR configuration. -pub struct Pesat<'a, F, C, H> +/// PESAT IOR configuration. Holds borrowed code + the trait CK; the +/// IOR is generic over any `MultiVectorCommitment` over `F`. +pub struct Pesat<'a, F, C, V> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, C: LinearCode, - H: MerkleHasher>, + V: MultiVectorCommitment, { pub code: &'a C, - pub hasher: &'a H, - pub _phantom: PhantomData<(F, H)>, + pub ck: &'a V::CommitterKey, + pub _phantom: PhantomData, } -impl<'a, F, C, H> IOR for Pesat<'a, F, C, H> +impl<'a, F, C, V> IOR for Pesat<'a, F, C, V> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, C: LinearCode, - H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + V: MultiVectorCommitment, + V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize, { const NAME: &'static str = "PESAT"; type Statement<'b> @@ -86,8 +92,8 @@ where type ReductionInputs = PesatReductionInputs; type ReducedStatement = PesatReducedStatement; type ProofString = (); - type ReducedWitness = PesatReducedWitness; - type VerifierOutputs = PesatVerifierOutputs; + type ReducedWitness = PesatReducedWitness; + type VerifierOutputs = PesatVerifierOutputs; fn reduce_statement<'b>( &self, @@ -117,12 +123,16 @@ where ) -> ProverTriple where 'a: 'b, - H: 'b, + V: 'b, { - let codewords = { + let codewords: Vec> = { let _s = tracing::info_span!("pesat.encode").entered(); count_ops!(EncodeCalls, witness.witnesses.len() as u64); - encode_codewords(self.code, witness.witnesses) + witness + .witnesses + .iter() + .map(|w| self.code.encode(w)) + .collect() }; let mus = codewords.iter().map(|f| f[0]).collect::>(); @@ -130,13 +140,19 @@ where let td_0 = { let _s = tracing::info_span!("pesat.merkle_commit").entered(); count_ops!(MerkleTreeBuilds); - let scheme = warp_scheme(self.hasher.clone(), self.code.code_len()); - scheme.commit(&codewords) + let (commitment, state) = + V::commit_multiple(self.ck, codewords.iter().map(|c| c.iter())) + .expect("pesat: commit_multiple failed"); + CommittedCodewords { + commitment, + state, + codewords: codewords.clone(), + } }; let taus = { let _s = tracing::info_span!("pesat.absorb_and_derive").entered(); - prover_state.prover_message(td_0.root()); + prover_state.prover_message(&td_0.commitment); prover_state.prover_messages(&mus); (0..statement.l1_first_fold_factor) @@ -170,9 +186,9 @@ where ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> where 'a: 'c, - H: 'c, + V: 'c, { - let rt_0: H::Digest = verifier_state.prover_message()?; + let rt_0: V::Commitment = verifier_state.prover_message()?; let mus: Vec = verifier_state.prover_messages_vec(statement.l1_first_fold_factor)?; let taus: Vec> = (0..statement.l1_first_fold_factor) .map(|_| { @@ -188,7 +204,7 @@ where taus_zero_check_challenges: taus, }, PesatVerifierOutputs { - rt_0_fresh_merkle_root: rt_0, + rt_0_fresh_commitment: rt_0, }, )) } diff --git a/src/protocol/iors/proximity.rs b/src/protocol/iors/proximity.rs index c4eb7c6..c3b9360 100644 --- a/src/protocol/iors/proximity.rs +++ b/src/protocol/iors/proximity.rs @@ -1,17 +1,18 @@ //! Proximity / shift-query IOR. //! //! Index queries on the committed oracles. Opens both the fresh PESAT -//! commitment and each accumulated commitment at the query positions, -//! producing one multi-opening proof per commitment and a flat table of -//! the codeword values at those positions. +//! commitment and each accumulated commitment at the query positions. +//! With the trait migration, opening proofs (auth paths + sibling +//! digests) are written into the spongefish transcript by +//! `V::open_multiple` rather than carried as separate proof fields. //! use ark_ff::Field; -use ark_mt::MerkleHasher; -use spongefish::{ProverState, VerifierState}; +use ark_vc::mvc::MultiVectorCommitment; +use spongefish::{Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; use crate::count_ops; -use crate::crypto::merkle::{warp_scheme, WarpCommitted, WarpProof}; +use crate::crypto::merkle::CommittedCodewords; use crate::error::VerifierError; use crate::protocol::ior::{ProverTriple, IOR}; use crate::protocol::oracles::indexed_merkle::IndexedOracle; @@ -22,62 +23,56 @@ pub struct ProximityStatement { pub l2_second_fold_factor: usize, pub t_num_queries: usize, /// Codeword length (`code.code_len()`), needed by both prover and - /// verifier to construct the `WarpScheme` used for open/check. + /// verifier. pub n_code_len: usize, } -pub struct ProximityProverInputs<'a, F, H> +pub struct ProximityProverInputs<'a, F, V> where F: Field, - H: MerkleHasher>, + V: MultiVectorCommitment, { - pub td_0_committed_codeword: &'a WarpCommitted, - pub acc_td_committed_codewords: &'a [WarpCommitted], + pub ck: &'a V::CommitterKey, + pub td_0_committed_codeword: &'a CommittedCodewords, + pub acc_td_committed_codewords: &'a [CommittedCodewords], } /// Verifier-side inputs. The IOR sees [`IndexedOracle`] handles, not -/// raw roots / opening proofs — IORs stay BCS-agnostic. +/// raw commitments / opening proofs — IORs stay BCS-agnostic. pub struct ProximityVerifierInputs<'a, F, O> where F: Field, O: IndexedOracle>, { - /// Handle for the fresh PESAT multi-vector commitment (m = l1). pub fresh: &'a O, - /// One handle per accumulated commitment (each with m = 1). pub acc: &'a [O], pub _f: PhantomData, } -pub struct ProximityProofString -where - F: Field, - H: MerkleHasher, -{ - /// Single multi-opening proof for the fresh PESAT commitment. - pub auth_0: WarpProof, - /// One multi-opening proof per accumulated commitment. - pub auth_j: Vec>, - /// Shift query answers: per-query × per-codeword. - /// Outer length = t (queries). Inner length = (l2 acc + l1 fresh). +/// Wire-format proof string. Auth paths + sibling digests now live in +/// the spongefish transcript (written by `V::open_multiple`); only the +/// shift-query answers remain as out-of-band data. +pub struct ProximityProofString { + /// Per-query × per-codeword. Outer length = t (queries). Inner + /// length = (l2 acc + l1 fresh). pub shift_query_answers: Vec>, } -/// Proximity IOR configuration. Holds the hasher value used by both -/// `open` (prover side) and `check` (verifier side). -pub struct Proximity<'a, F, H> +/// Proximity IOR configuration. Holds the trait CK used by `open_multiple`. +pub struct Proximity<'a, F, V> where F: Field, - H: MerkleHasher>, + V: MultiVectorCommitment, { - pub hasher: &'a H, + pub ck: &'a V::CommitterKey, pub _phantom: PhantomData, } -impl<'a, F, H> IOR for Proximity<'a, F, H> +impl<'a, F, V> IOR for Proximity<'a, F, V> where F: Field, - H: MerkleHasher>, + V: MultiVectorCommitment, + V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize, { const NAME: &'static str = "Proximity"; @@ -90,20 +85,17 @@ where where Self: 'b; type ProverInputs<'b> - = ProximityProverInputs<'b, F, H> + = ProximityProverInputs<'b, F, V> where Self: 'b; type VerifierInputs<'b> - = ProximityVerifierInputs< - 'b, - F, - crate::protocol::oracles::indexed_merkle::MerkleIndexedOracle<'b, F, H>, - > + = + ProximityVerifierInputs<'b, F, crate::protocol::oracles::indexed_merkle::ValidatedOracle> where Self: 'b; type ReductionInputs = (); type ReducedStatement = (); - type ProofString = ProximityProofString; + type ProofString = ProximityProofString; type ReducedWitness = (); type VerifierOutputs = (); @@ -127,7 +119,7 @@ where )] fn prove_inner<'b>( &self, - _prover_state: &mut ProverState, + prover_state: &mut ProverState, statement: &Self::Statement<'b>, _witness: &Self::Witness<'b>, inputs: &Self::ProverInputs<'b>, @@ -135,38 +127,64 @@ where where Self: 'b, 'a: 'b, - H: 'b, + V: 'b, { let leaf_positions = &statement.queries.leaf_positions; - // ark-mt's `open()` requires strictly-sorted, unique indices. Query - // positions can repeat or arrive unsorted; deduplicate for the - // merkle opening, but keep `shift_query_answers` in original query - // order so Batching can index by query. + // Trait `open_multiple` requires sorted unique indices. let mut sorted_unique = leaf_positions.clone(); sorted_unique.sort_unstable(); sorted_unique.dedup(); - let scheme = warp_scheme(self.hasher.clone(), statement.n_code_len); + // Helper: column-tuple values at the sorted positions for one + // commitment's codewords. + let column_tuples = |codewords: &[Vec]| -> Vec> { + sorted_unique + .iter() + .map(|&i| codewords.iter().map(|c| c[i]).collect()) + .collect() + }; - let auth_0 = { + { let _s = tracing::info_span!("proximity.auth_0").entered(); count_ops!(MerklePathsGenerated, sorted_unique.len() as u64); - scheme.open(inputs.td_0_committed_codeword, &sorted_unique) - }; + let values = column_tuples(&inputs.td_0_committed_codeword.codewords); + V::open_multiple( + inputs.ck, + inputs + .td_0_committed_codeword + .codewords + .iter() + .map(|c| c.iter()), + &inputs.td_0_committed_codeword.commitment, + sorted_unique.iter().copied(), + values.into_iter(), + &inputs.td_0_committed_codeword.state, + prover_state, + ) + .expect("proximity: open_multiple (fresh) failed"); + } - let auth_j: Vec> = { + { let _s = tracing::info_span!("proximity.auth_j").entered(); count_ops!( MerklePathsGenerated, (inputs.acc_td_committed_codewords.len() * sorted_unique.len()) as u64 ); - inputs - .acc_td_committed_codewords - .iter() - .map(|td| scheme.open(td, &sorted_unique)) - .collect() - }; + for td in inputs.acc_td_committed_codewords.iter() { + let values = column_tuples(&td.codewords); + V::open_multiple( + inputs.ck, + td.codewords.iter().map(|c| c.iter()), + &td.commitment, + sorted_unique.iter().copied(), + values.into_iter(), + &td.state, + prover_state, + ) + .expect("proximity: open_multiple (acc) failed"); + } + } // Shift query answers: per query position, values across // (acc_codewords ++ fresh_codewords). @@ -175,19 +193,19 @@ where let total_codewords = inputs .acc_td_committed_codewords .iter() - .map(|td| td.num_codewords()) + .map(|td| td.codewords.len()) .sum::() - + inputs.td_0_committed_codeword.num_codewords(); + + inputs.td_0_committed_codeword.codewords.len(); let mut answers = vec![vec![F::default(); total_codewords]; leaf_positions.len()]; for (qi, idx) in leaf_positions.iter().enumerate() { let mut col = 0usize; for td in inputs.acc_td_committed_codewords.iter() { - for cw in td.codewords() { + for cw in &td.codewords { answers[qi][col] = cw[*idx]; col += 1; } } - for cw in inputs.td_0_committed_codeword.codewords() { + for cw in &inputs.td_0_committed_codeword.codewords { answers[qi][col] = cw[*idx]; col += 1; } @@ -198,8 +216,6 @@ where Ok(( (), ProximityProofString { - auth_0, - auth_j, shift_query_answers, }, (), @@ -220,16 +236,16 @@ where where Self: 'c, 'a: 'c, - H: 'c, + V: 'c, { - // Arity check: number of accumulator openings must match l2. + // Arity check. (inputs.acc.len() == statement.l2_second_fold_factor) .then_some(()) .ok_or(VerifierError::NumL2Instances)?; - // Validate each oracle handle. The handle is a partial function: - // validate() runs the (lazy, memoized) BCS check internally — - // this IOR stays BCS-agnostic. + // Validation already happened upstream (orchestrator called + // V::check_multiple before invoking this IOR). Handles are + // pre-validated; this IOR is BCS-agnostic. inputs .fresh .validate() diff --git a/src/protocol/iors/twin_constraint.rs b/src/protocol/iors/twin_constraint.rs index 9554444..21f9406 100644 --- a/src/protocol/iors/twin_constraint.rs +++ b/src/protocol/iors/twin_constraint.rs @@ -3,8 +3,8 @@ //! Paired spec: `docs/paper-mods/mod1_oracle.tex`. use ark_ff::{Field, PrimeField}; -use ark_mt::MerkleHasher; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; +use ark_vc::mvc::MultiVectorCommitment; use effsc::{ coefficient_sumcheck::RoundPolyEvaluator, folding::protogalaxy, @@ -153,8 +153,12 @@ impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { } } -pub struct TwinConstraintStatement { - pub acc_instance: AccumulatorInstance, +pub struct TwinConstraintStatement +where + F: Field, + V: MultiVectorCommitment, +{ + pub acc_instance: AccumulatorInstance, pub l1_mus_codeword_first_coords: Vec, pub l1_taus_zero_check_challenges: Vec>, pub log_l: usize, @@ -236,24 +240,24 @@ pub struct TwinConstraintReducedWitness { pub z_witness_assignment: Vec, } -pub struct TwinConstraint<'a, F, H> +pub struct TwinConstraint<'a, F, V> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - H: MerkleHasher, + V: MultiVectorCommitment, { pub r1cs: &'a R1CSConstraints, - pub _phantom: PhantomData, + pub _phantom: PhantomData, } -impl<'a, F, H> IOR for TwinConstraint<'a, F, H> +impl<'a, F, V> IOR for TwinConstraint<'a, F, V> where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, - H: MerkleHasher, + V: MultiVectorCommitment, { const NAME: &'static str = "TwinConstraint"; type Statement<'b> - = TwinConstraintStatement + = TwinConstraintStatement where Self: 'b; type Witness<'b> @@ -335,7 +339,7 @@ where where Self: 'b, 'a: 'b, - H: 'b, + V: 'b, { let l1 = inputs.fresh_codewords.len(); let log_l = statement.log_l; @@ -449,7 +453,7 @@ where where Self: 'c, 'a: 'c, - H: 'c, + V: 'c, { let log_l = statement.log_l; let log_n = statement.log_n; diff --git a/src/protocol/oracles/indexed_merkle.rs b/src/protocol/oracles/indexed_merkle.rs index 6ee5ea6..99ec523 100644 --- a/src/protocol/oracles/indexed_merkle.rs +++ b/src/protocol/oracles/indexed_merkle.rs @@ -1,12 +1,12 @@ -//! Partial-function view of a Merkle-committed indexed oracle: -//! `query(i)` returns `Some(value)` iff the opening at `i` validates. - -use std::cell::OnceCell; +//! Pre-validated lookup view of an opened indexed oracle. +//! +//! With the trait migration, opening proofs live in the spongefish +//! transcript and `V::check_multiple` consumes them at a specific +//! point in the transcript. The orchestrator validates upstream and +//! hands IORs a `ValidatedOracle` — a sorted (index, value) table the +//! IOR queries without re-running the BCS check. use ark_ff::Field; -use ark_mt::{multi_vector::MultiVectorOpening, MerkleHasher}; - -use crate::crypto::merkle::{WarpProof, WarpScheme}; pub trait IndexedOracle { fn query(&self, i: usize) -> Option; @@ -15,68 +15,27 @@ pub trait IndexedOracle { } } -pub struct MerkleIndexedOracle<'a, F, H> -where - F: Field + Clone, - H: MerkleHasher>, -{ - pub scheme: WarpScheme, - pub root: &'a H::Digest, - pub proof: &'a WarpProof, +pub struct ValidatedOracle { pub sorted_indices: Vec, pub values_by_index: Vec>, - validated: OnceCell, } -impl<'a, F, H> MerkleIndexedOracle<'a, F, H> -where - F: Field + Clone, - H: MerkleHasher>, -{ - pub fn new( - scheme: WarpScheme, - root: &'a H::Digest, - proof: &'a WarpProof, - sorted_indices: Vec, - values_by_index: Vec>, - ) -> Self { +impl ValidatedOracle { + pub fn new(sorted_indices: Vec, values_by_index: Vec>) -> Self { Self { - scheme, - root, - proof, sorted_indices, values_by_index, - validated: OnceCell::new(), } } - - fn run_validation(&self) -> bool { - let opening = match MultiVectorOpening::new( - self.sorted_indices.clone(), - self.values_by_index.clone(), - ) { - Ok(o) => o, - Err(_) => return false, - }; - self.scheme.check(self.root, &opening, self.proof) - } } -impl<'a, F, H> IndexedOracle> for MerkleIndexedOracle<'a, F, H> -where - F: Field + Clone, - H: MerkleHasher>, -{ +impl IndexedOracle> for ValidatedOracle { fn query(&self, i: usize) -> Option> { - let valid = *self.validated.get_or_init(|| self.run_validation()); - if !valid { - return None; - } let pos = self.sorted_indices.binary_search(&i).ok()?; Some(self.values_by_index[pos].clone()) } fn validate(&self) -> bool { - *self.validated.get_or_init(|| self.run_validation()) + true } } diff --git a/src/protocol/transcript/prover.rs b/src/protocol/transcript/prover.rs index bfc4d30..0091497 100644 --- a/src/protocol/transcript/prover.rs +++ b/src/protocol/transcript/prover.rs @@ -1,5 +1,5 @@ use ark_ff::Field; -use ark_mt::MerkleHasher; +use ark_vc::mvc::MultiVectorCommitment; use spongefish::{Encoding, ProverState}; use crate::warp::AccumulatorInstance; @@ -17,15 +17,15 @@ pub fn absorb_instances>( } // absorb an AccumulatorInstance into the transcript -impl AccumulatorInstance +impl AccumulatorInstance where F: Field + Encoding<[u8]>, - H: MerkleHasher, - H::Digest: Encoding<[u8]>, + V: MultiVectorCommitment, + V::Commitment: Encoding<[u8]> + spongefish::NargSerialize, { pub fn absorb_into(&self, prover_state: &mut ProverState) { - for digest in &self.rt_merkle_roots { - prover_state.prover_message(digest); + for commitment in &self.rt_commitments { + prover_state.prover_message(commitment); } for alpha in &self.alpha_fold_vectors { diff --git a/src/protocol/transcript/verifier.rs b/src/protocol/transcript/verifier.rs index ba28561..1d91489 100644 --- a/src/protocol/transcript/verifier.rs +++ b/src/protocol/transcript/verifier.rs @@ -1,42 +1,42 @@ use ark_ff::Field; -use ark_mt::MerkleHasher; +use ark_vc::mvc::MultiVectorCommitment; use spongefish::{Decoding, Encoding, NargDeserialize, VerificationResult, VerifierState}; use crate::warp::AccumulatorInstance; // (l1 instances, accumulated instance) -pub type ParsedStatement = (Vec>, AccumulatorInstance); +pub type ParsedStatement = (Vec>, AccumulatorInstance); // parse l1 plain instances + an AccumulatorInstance from the transcript -pub fn parse_statement( +pub fn parse_statement( verifier_state: &mut VerifierState<'_>, l1: usize, l2: usize, instance_len: usize, log_n: usize, log_m: usize, -) -> VerificationResult> +) -> VerificationResult> where F: Field + NargDeserialize + Encoding<[u8]> + Decoding<[u8]>, - H: MerkleHasher, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, + V: MultiVectorCommitment, + V::Commitment: Encoding<[u8]> + NargDeserialize, { let l1_xs: Vec> = (0..l1) .map(|_| verifier_state.prover_messages_vec(instance_len)) .collect::>()?; let acc = - AccumulatorInstance::::parse_from(verifier_state, l2, log_n, log_m, instance_len)?; + AccumulatorInstance::::parse_from(verifier_state, l2, log_n, log_m, instance_len)?; Ok((l1_xs, acc)) } // parse an AccumulatorInstance from the verifier transcript -impl AccumulatorInstance +impl AccumulatorInstance where F: Field + NargDeserialize + Encoding<[u8]> + Decoding<[u8]>, - H: MerkleHasher, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, + V: MultiVectorCommitment, + V::Commitment: Encoding<[u8]> + NargDeserialize, { pub fn parse_from( verifier_state: &mut VerifierState<'_>, @@ -45,8 +45,8 @@ where log_m: usize, instance_len: usize, ) -> VerificationResult { - let rt: Vec = (0..l2) - .map(|_| verifier_state.prover_message::()) + let rt: Vec = (0..l2) + .map(|_| verifier_state.prover_message::()) .collect::>()?; let alpha: Vec> = (0..l2) @@ -72,7 +72,7 @@ where let eta: Vec = verifier_state.prover_messages_vec(l2)?; Ok(Self { - rt_merkle_roots: rt, + rt_commitments: rt, alpha_fold_vectors: alpha, mu_claimed_evals: mu, beta_twin_pairs, diff --git a/src/serialize.rs b/src/serialize.rs index 6836b0b..8d494eb 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -1,29 +1,27 @@ use ark_ff::Field; -use ark_mt::MerkleHasher; use ark_serialize::{CanonicalSerialize, Compress, SerializationError, Valid, Write}; +use ark_vc::mvc::MultiVectorCommitment; -use crate::crypto::merkle::WarpProof; use crate::warp::{AccumulatorInstance, AccumulatorWitness, WARPProof}; // `AccumulatorInstance` and `WARPProof` carry generic associated types -// (`H::Digest`, `WarpProof`) whose serializability is not implied by -// `H: MerkleHasher`. Putting the `CanonicalSerialize` bound in a -// separate `impl` block (rather than on the struct) keeps the bulk of -// the IOR / orchestrator code free of that dep — only the size-printing -// path pulls it in. +// (`V::Commitment`) whose serializability isn't implied by the trait +// itself. Putting the bound in a separate `impl` block (rather than on +// the struct) keeps the bulk of the IOR / orchestrator code free of that +// dep — only the size-printing path pulls it in. -impl CanonicalSerialize for AccumulatorInstance +impl CanonicalSerialize for AccumulatorInstance where F: Field + CanonicalSerialize, - H: MerkleHasher, - H::Digest: CanonicalSerialize, + V: MultiVectorCommitment, + V::Commitment: CanonicalSerialize, { fn serialize_with_mode( &self, mut writer: W, compress: Compress, ) -> Result<(), SerializationError> { - self.rt_merkle_roots + self.rt_commitments .serialize_with_mode(&mut writer, compress)?; self.alpha_fold_vectors .serialize_with_mode(&mut writer, compress)?; @@ -41,7 +39,7 @@ where fn serialized_size(&self, compress: Compress) -> usize { let taus: Vec<&Vec> = self.beta_twin_pairs.iter().map(|p| &p.tau).collect(); let xs: Vec<&Vec> = self.beta_twin_pairs.iter().map(|p| &p.x).collect(); - self.rt_merkle_roots.serialized_size(compress) + self.rt_commitments.serialized_size(compress) + self.alpha_fold_vectors.serialized_size(compress) + self.mu_claimed_evals.serialized_size(compress) + taus.serialized_size(compress) @@ -50,30 +48,29 @@ where } } -impl Valid for AccumulatorInstance +impl Valid for AccumulatorInstance where F: Field + CanonicalSerialize, - H: MerkleHasher, - H::Digest: CanonicalSerialize, + V: MultiVectorCommitment, + V::Commitment: CanonicalSerialize, { fn check(&self) -> Result<(), SerializationError> { Ok(()) } } -impl CanonicalSerialize for WARPProof +impl CanonicalSerialize for WARPProof where F: Field + CanonicalSerialize, - H: MerkleHasher, - H::Digest: CanonicalSerialize, - WarpProof: CanonicalSerialize, + V: MultiVectorCommitment, + V::Commitment: CanonicalSerialize, { fn serialize_with_mode( &self, mut writer: W, compress: Compress, ) -> Result<(), SerializationError> { - self.rt_0_fresh_merkle_root + self.rt_0_fresh_commitment .serialize_with_mode(&mut writer, compress)?; self.mu_i_first_codeword_coords .serialize_with_mode(&mut writer, compress)?; @@ -81,30 +78,25 @@ where .serialize_with_mode(&mut writer, compress)?; self.nu_i_oracle_evals .serialize_with_mode(&mut writer, compress)?; - self.auth_0.serialize_with_mode(&mut writer, compress)?; - self.auth_j.serialize_with_mode(&mut writer, compress)?; self.shift_query_answers .serialize_with_mode(&mut writer, compress)?; Ok(()) } fn serialized_size(&self, compress: Compress) -> usize { - self.rt_0_fresh_merkle_root.serialized_size(compress) + self.rt_0_fresh_commitment.serialized_size(compress) + self.mu_i_first_codeword_coords.serialized_size(compress) + self.nu_0_oracle_eval.serialized_size(compress) + self.nu_i_oracle_evals.serialized_size(compress) - + self.auth_0.serialized_size(compress) - + self.auth_j.serialized_size(compress) + self.shift_query_answers.serialized_size(compress) } } -impl Valid for WARPProof +impl Valid for WARPProof where F: Field + CanonicalSerialize, - H: MerkleHasher, - H::Digest: CanonicalSerialize, - WarpProof: CanonicalSerialize, + V: MultiVectorCommitment, + V::Commitment: CanonicalSerialize, { fn check(&self) -> Result<(), SerializationError> { Ok(()) @@ -115,10 +107,10 @@ where /// from the serialized form: only `w` ships across the wire — the tree /// is reconstructable by re-encoding `w`. Used for proof-size reporting, /// not on-the-wire serialization. -pub fn acc_witness_size(acc_witness: &AccumulatorWitness, compress: Compress) -> usize +pub fn acc_witness_size(acc_witness: &AccumulatorWitness, compress: Compress) -> usize where F: Field + CanonicalSerialize, - H: MerkleHasher>, + V: MultiVectorCommitment, { acc_witness.w_witnesses.serialized_size(compress) } diff --git a/src/warp/accumulator.rs b/src/warp/accumulator.rs index 69ce2d2..7204e0c 100644 --- a/src/warp/accumulator.rs +++ b/src/warp/accumulator.rs @@ -1,7 +1,7 @@ use ark_ff::Field; -use ark_mt::MerkleHasher; +use ark_vc::mvc::MultiVectorCommitment; -use crate::crypto::merkle::WarpCommitted; +use crate::crypto::merkle::CommittedCodewords; /// Per-instance β coordinates absorbed into the accumulator: the `(τ, x)` /// twin pair. Replaces the old parallel-`Vec` tuple shape which let callers @@ -13,19 +13,43 @@ pub struct BetaTwinPair { } /// Public part of an accumulated claim: `(rt, α, μ, (τ, x), η)` in the paper. -#[derive(Clone)] -pub struct AccumulatorInstance { - pub rt_merkle_roots: Vec, +pub struct AccumulatorInstance +where + F: Field, + V: MultiVectorCommitment, +{ + pub rt_commitments: Vec, pub alpha_fold_vectors: Vec>, pub mu_claimed_evals: Vec, pub beta_twin_pairs: Vec>, pub eta_predicate_evals: Vec, } -impl AccumulatorInstance { +impl Clone for AccumulatorInstance +where + F: Field, + V: MultiVectorCommitment, + V::Commitment: Clone, +{ + fn clone(&self) -> Self { + Self { + rt_commitments: self.rt_commitments.clone(), + alpha_fold_vectors: self.alpha_fold_vectors.clone(), + mu_claimed_evals: self.mu_claimed_evals.clone(), + beta_twin_pairs: self.beta_twin_pairs.clone(), + eta_predicate_evals: self.eta_predicate_evals.clone(), + } + } +} + +impl AccumulatorInstance +where + F: Field, + V: MultiVectorCommitment, +{ pub fn empty() -> Self { Self { - rt_merkle_roots: vec![], + rt_commitments: vec![], alpha_fold_vectors: vec![], mu_claimed_evals: vec![], beta_twin_pairs: vec![], @@ -34,7 +58,7 @@ impl AccumulatorInstance { } pub fn extend(mut self, other: Self) -> Self { - self.rt_merkle_roots.extend(other.rt_merkle_roots); + self.rt_commitments.extend(other.rt_commitments); self.alpha_fold_vectors.extend(other.alpha_fold_vectors); self.mu_claimed_evals.extend(other.mu_claimed_evals); self.beta_twin_pairs.extend(other.beta_twin_pairs); @@ -44,20 +68,21 @@ impl AccumulatorInstance { } /// Private part of an accumulated claim: `(td, w)` in the paper. -pub struct AccumulatorWitness +pub struct AccumulatorWitness where F: Field, - H: MerkleHasher>, + V: MultiVectorCommitment, { - pub td_committed_codewords: Vec>, + pub td_committed_codewords: Vec>, pub w_witnesses: Vec>, } -impl Clone for AccumulatorWitness +impl Clone for AccumulatorWitness where F: Field, - H: MerkleHasher>, - WarpCommitted: Clone, + V: MultiVectorCommitment, + V::Commitment: Clone, + V::CommitmentState: Clone, { fn clone(&self) -> Self { Self { @@ -67,10 +92,10 @@ where } } -impl AccumulatorWitness +impl AccumulatorWitness where F: Field, - H: MerkleHasher>, + V: MultiVectorCommitment, { pub fn empty() -> Self { Self { diff --git a/src/warp/decide.rs b/src/warp/decide.rs index 31794cf..4fc0205 100644 --- a/src/warp/decide.rs +++ b/src/warp/decide.rs @@ -1,43 +1,44 @@ use ark_codes::traits::LinearCode; use ark_ff::{Field, PrimeField}; -use ark_mt::MerkleHasher; use ark_poly::{DenseMultilinearExtension, Polynomial}; use ark_std::log2; +use ark_vc::mvc::MultiVectorCommitment; +use ark_vc::vc::VectorCommitment; use effsc::hypercube::compute_hypercube_eq_evals; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize}; -use crate::crypto::merkle::warp_scheme; use crate::error::{DeciderError, WARPError}; use crate::relations::PolyPredicate; use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; use crate::warp::scheme::WARP; -impl WARP +impl WARP where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, P: Clone + PolyPredicate, C: LinearCode + Clone, - H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, + V: MultiVectorCommitment, + V::Commitment: Clone + Eq, { pub fn decide( &self, - acc_witness: AccumulatorWitness, - acc_instance: AccumulatorInstance, + acc_witness: AccumulatorWitness, + acc_instance: AccumulatorInstance, ) -> Result<(), WARPError> { - let acc_codeword = &acc_witness.td_committed_codewords[0].codewords()[0]; + let acc_codeword = &acc_witness.td_committed_codewords[0].codewords[0]; let computed_f = self.params.code.encode(&acc_witness.w_witnesses[0]); (acc_codeword == &computed_f) .then_some(()) .ok_or(DeciderError::EncodedWitness)?; - let scheme = warp_scheme::(self.params.hasher.clone(), self.params.code.code_len()); - let recomputed = scheme.commit(std::slice::from_ref(&computed_f)); - (acc_instance.rt_merkle_roots[0] == *recomputed.root()) + let (recomputed_commitment, _state) = + ::commit(&self.params.ck, computed_f.iter()) + .map_err(|_| DeciderError::MerkleRoot)?; + (acc_instance.rt_commitments[0] == recomputed_commitment) .then_some(()) .ok_or(DeciderError::MerkleRoot)?; - (acc_witness.td_committed_codewords[0].root() == recomputed.root()) + (acc_witness.td_committed_codewords[0].commitment == recomputed_commitment) .then_some(()) .ok_or(DeciderError::MerkleTrapDoor)?; diff --git a/src/warp/params.rs b/src/warp/params.rs index 0481c68..399a336 100644 --- a/src/warp/params.rs +++ b/src/warp/params.rs @@ -1,16 +1,23 @@ use ark_codes::traits::LinearCode; use ark_ff::Field; -use ark_mt::MerkleHasher; +use ark_vc::mvc::MultiVectorCommitment; use std::marker::PhantomData; use crate::config::WARPConfig; use crate::relations::PolyPredicate; /// Shared configuration used by all IORs. -pub struct WARPParams, C: LinearCode + Clone, H: MerkleHasher> { +pub struct WARPParams +where + F: Field, + P: PolyPredicate, + C: LinearCode + Clone, + V: MultiVectorCommitment, +{ pub(crate) _phantom_f: PhantomData, pub config: WARPConfig, pub code: C, pub predicate: P, - pub hasher: H, + pub ck: V::CommitterKey, + pub vk: V::VerifierKey, } diff --git a/src/warp/proof.rs b/src/warp/proof.rs index e1832a4..94c5af8 100644 --- a/src/warp/proof.rs +++ b/src/warp/proof.rs @@ -1,46 +1,46 @@ use ark_ff::Field; -use ark_mt::MerkleHasher; +use ark_vc::mvc::MultiVectorCommitment; -use crate::crypto::merkle::WarpProof; use crate::error::ProverError; use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; -/// Proof produced by the WARP accumulation prover. Corresponds to -/// `(rt₀, μᵢ, ν₀, νᵢ, auth₀, authⱼ, f_i(x_j))` in the paper. -pub struct WARPProof { - pub rt_0_fresh_merkle_root: H::Digest, +/// Proof produced by the WARP accumulation prover. Auth paths and +/// sibling digests now live in the spongefish transcript (the trait's +/// `open_multiple` writes them via `prover_state.prover_message`), so +/// they no longer appear here as separate fields. +pub struct WARPProof +where + F: Field, + V: MultiVectorCommitment, +{ + pub rt_0_fresh_commitment: V::Commitment, pub mu_i_first_codeword_coords: Vec, pub nu_0_oracle_eval: F, pub nu_i_oracle_evals: Vec, - pub auth_0: WarpProof, - pub auth_j: Vec>, pub shift_query_answers: Vec>, } -impl Clone for WARPProof +impl Clone for WARPProof where F: Field, - H: MerkleHasher, - H::Digest: Clone, - WarpProof: Clone, + V: MultiVectorCommitment, + V::Commitment: Clone, { fn clone(&self) -> Self { Self { - rt_0_fresh_merkle_root: self.rt_0_fresh_merkle_root.clone(), + rt_0_fresh_commitment: self.rt_0_fresh_commitment.clone(), mu_i_first_codeword_coords: self.mu_i_first_codeword_coords.clone(), nu_0_oracle_eval: self.nu_0_oracle_eval, nu_i_oracle_evals: self.nu_i_oracle_evals.clone(), - auth_0: self.auth_0.clone(), - auth_j: self.auth_j.clone(), shift_query_answers: self.shift_query_answers.clone(), } } } -pub type ProveResult = Result< +pub type ProveResult = Result< ( - (AccumulatorInstance, AccumulatorWitness), - WARPProof, + (AccumulatorInstance, AccumulatorWitness), + WARPProof, ), ProverError, >; diff --git a/src/warp/prove.rs b/src/warp/prove.rs index 6f3d18e..c30492b 100644 --- a/src/warp/prove.rs +++ b/src/warp/prove.rs @@ -1,7 +1,7 @@ use ark_codes::traits::LinearCode; use ark_ff::{Field, PrimeField}; -use ark_mt::MerkleHasher; use ark_std::log2; +use ark_vc::mvc::MultiVectorCommitment; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; use std::marker::PhantomData; @@ -33,21 +33,21 @@ use crate::warp::keys::WARPProverKey; use crate::warp::proof::{ProveResult, WARPProof}; use crate::warp::scheme::WARP; -impl WARP +impl WARP where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, P: Clone + PolyPredicate, C: LinearCode + Clone, - H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, + V: MultiVectorCommitment, + V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize + Clone, { fn validate_prover_inputs( &self, pk: &WARPProverKey

    , instances: &[Vec], witnesses: &[Vec], - acc_instance: &AccumulatorInstance, - acc_witness: &AccumulatorWitness, + acc_instance: &AccumulatorInstance, + acc_witness: &AccumulatorWitness, ) -> Result<(), ProverError> { if instances.len() < 2 { return Err(ProverError::InsufficientInstances { @@ -60,9 +60,9 @@ where witnesses: witnesses.len(), }); } - if acc_witness.td_committed_codewords.len() != acc_instance.rt_merkle_roots.len() { + if acc_witness.td_committed_codewords.len() != acc_instance.rt_commitments.len() { return Err(ProverError::AccumulatorShapeMismatch { - instances: acc_instance.rt_merkle_roots.len(), + instances: acc_instance.rt_commitments.len(), roots: acc_witness.td_committed_codewords.len(), }); } @@ -107,9 +107,9 @@ where prover_state: &mut ProverState, witnesses: Vec>, instances: Vec>, - acc_instance: AccumulatorInstance, - acc_witness: AccumulatorWitness, - ) -> ProveResult { + acc_instance: AccumulatorInstance, + acc_witness: AccumulatorWitness, + ) -> ProveResult { self.validate_prover_inputs(&pk, &instances, &witnesses, &acc_instance, &acc_witness)?; let log_l = log2(self.params.config.l_total_fold_factor()) as usize; @@ -118,16 +118,12 @@ where let log_n = log2(n_code_len) as usize; let n_minus_k = pk.n_num_variables - pk.k_num_witness_vars; - // Domain-separation for the FS-affecting IOR sequence. Order here - // must match `WARP::verify`. (Proximity is FS-transparent and - // intentionally omitted; its position is allowed to differ between - // prover and verifier without affecting soundness.) absorb_protocol_map_prover( prover_state, &[ - Pesat::::NAME, - TwinConstraint::::NAME, - Bridge::::NAME, + Pesat::::NAME, + TwinConstraint::::NAME, + Bridge::::NAME, Ood::::NAME, SampleQueries::::NAME, Batching::::NAME, @@ -141,23 +137,23 @@ where td_committed_codewords: acc_tds, w_witnesses: acc_ws, } = acc_witness; - let acc_fs: Vec> = acc_tds.iter().map(|td| td.codewords()[0].clone()).collect(); + let acc_fs: Vec> = acc_tds.iter().map(|td| td.codewords[0].clone()).collect(); - let pesat_ior = Pesat:: { + let pesat_ior = Pesat:: { code: &self.params.code, - hasher: &self.params.hasher, + ck: &self.params.ck, _phantom: PhantomData, }; - let twin_constraint_ior = TwinConstraint:: { + let twin_constraint_ior = TwinConstraint:: { r1cs: self.params.predicate.constraints(), _phantom: PhantomData, }; - let bridge_ior = Bridge::::default(); + let bridge_ior = Bridge::::default(); let ood_ior = Ood::::default(); let sample_queries_ior = SampleQueries::::default(); let batching_ior = Batching::::default(); - let proximity_ior = Proximity:: { - hasher: &self.params.hasher, + let proximity_ior = Proximity:: { + ck: &self.params.ck, _phantom: PhantomData, }; @@ -225,7 +221,7 @@ where BridgeReducedStatement { eta_predicate_eval, nu_0_oracle_eval, - td_new_root: _, + td_new_commitment: _, }, proof: _, witness: @@ -249,8 +245,7 @@ where }, inputs: BridgeProverInputs { predicate: &self.params.predicate, - hasher: &self.params.hasher, - code_len: n_code_len, + ck: &self.params.ck, _f: PhantomData, }, )?; @@ -307,12 +302,9 @@ where let IorProveResult { reduced: _, - proof: - ProximityProofString { - auth_0, - auth_j, - shift_query_answers, - }, + proof: ProximityProofString { + shift_query_answers, + }, witness: _, } = prove_ior!( proximity_ior, @@ -325,6 +317,7 @@ where }, witness: (), inputs: ProximityProverInputs { + ck: &self.params.ck, td_0_committed_codeword: &td_0_committed_codeword, acc_td_committed_codewords: &acc_tds, }, @@ -335,7 +328,7 @@ where nu_i_oracle_evals.extend(answers); let new_acc_instance = AccumulatorInstance { - rt_merkle_roots: vec![td_new.root().clone()], + rt_commitments: vec![td_new.commitment.clone()], alpha_fold_vectors: vec![alpha_sumcheck_challenges], mu_claimed_evals: vec![mu_claimed_eval], beta_twin_pairs: vec![BetaTwinPair { @@ -349,12 +342,10 @@ where w_witnesses: vec![new_w], }; let proof = WARPProof { - rt_0_fresh_merkle_root: td_0_committed_codeword.root().clone(), + rt_0_fresh_commitment: td_0_committed_codeword.commitment.clone(), mu_i_first_codeword_coords: mus_codeword_first_coords, nu_0_oracle_eval, nu_i_oracle_evals, - auth_0, - auth_j, shift_query_answers, }; diff --git a/src/warp/scheme.rs b/src/warp/scheme.rs index 3420d8b..97753f4 100644 --- a/src/warp/scheme.rs +++ b/src/warp/scheme.rs @@ -1,6 +1,6 @@ use ark_codes::traits::LinearCode; use ark_ff::{Field, PrimeField}; -use ark_mt::MerkleHasher; +use ark_vc::mvc::MultiVectorCommitment; use spongefish::{ Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerificationResult, }; @@ -11,36 +11,49 @@ use crate::relations::PolyPredicate; use crate::warp::keys::{WARPProverKey, WARPVerifierKey}; use crate::warp::params::WARPParams; -pub struct WARP, C: LinearCode + Clone, H: MerkleHasher> { - pub params: WARPParams, +pub struct WARP +where + F: Field, + P: PolyPredicate, + C: LinearCode + Clone, + V: MultiVectorCommitment, +{ + pub params: WARPParams, } -impl WARP +impl WARP where F: Field, P: Clone + PolyPredicate, C: LinearCode + Clone, - H: MerkleHasher>, + V: MultiVectorCommitment, { - pub fn new(config: WARPConfig, code: C, predicate: P, hasher: H) -> WARP { + pub fn new( + config: WARPConfig, + code: C, + predicate: P, + ck: V::CommitterKey, + vk: V::VerifierKey, + ) -> WARP { Self { params: WARPParams { _phantom_f: PhantomData, config, code, predicate, - hasher, + ck, + vk, }, } } } -impl WARP +impl WARP where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, P: Clone + PolyPredicate, C: LinearCode + Clone, - H: MerkleHasher>, + V: MultiVectorCommitment, { pub fn index( prover_state: &mut ProverState, diff --git a/src/warp/verify.rs b/src/warp/verify.rs index 329a2df..14dda1f 100644 --- a/src/warp/verify.rs +++ b/src/warp/verify.rs @@ -1,12 +1,12 @@ use ark_codes::traits::LinearCode; use ark_ff::{Field, PrimeField}; -use ark_mt::MerkleHasher; use ark_std::log2; +use ark_vc::mvc::MultiVectorCommitment; use effsc::hypercube::compute_hypercube_eq_evals; +use rand_core::OsRng; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, VerifierState}; use std::marker::PhantomData; -use crate::crypto::merkle::warp_scheme; use crate::error::VerifierError; use crate::protocol::ior::{absorb_protocol_map_verifier, IorVerifyResult, IOR}; use crate::protocol::iors::{ @@ -18,7 +18,7 @@ use crate::protocol::iors::{ sample_queries::{SampleQueries, SampleQueriesReducedStatement, SampleQueriesStatement}, twin_constraint::{TwinConstraint, TwinConstraintReducedStatement, TwinConstraintStatement}, }; -use crate::protocol::oracles::indexed_merkle::MerkleIndexedOracle; +use crate::protocol::oracles::indexed_merkle::ValidatedOracle; use crate::protocol::transcript::parse_statement; use crate::relations::PolyPredicate; use crate::utils::{concat_slices, scale_and_sum}; @@ -28,20 +28,20 @@ use crate::warp::keys::WARPVerifierKey; use crate::warp::proof::WARPProof; use crate::warp::scheme::WARP; -impl WARP +impl WARP where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, P: Clone + PolyPredicate, C: LinearCode + Clone, - H: MerkleHasher>, - H::Digest: Encoding<[u8]> + Decoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, + V: MultiVectorCommitment, + V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize + Clone, { pub fn verify<'a>( &self, vk: WARPVerifierKey, verifier_state: &mut VerifierState<'a>, - acc_instance: AccumulatorInstance, - proof: WARPProof, + acc_instance: AccumulatorInstance, + proof: WARPProof, ) -> Result<(), VerifierError> { let log_l = log2(self.params.config.l_total_fold_factor()) as usize; let log_m = log2(vk.m_num_constraints) as usize; @@ -49,21 +49,19 @@ where let log_n = log2(n_code_len) as usize; let n_minus_k = vk.n_num_variables - vk.k_num_witness_vars; - // Paired with `WARP::prove`. Catches reorder/swap of FS-affecting - // IORs between prover and verifier. absorb_protocol_map_verifier( verifier_state, &[ - Pesat::::NAME, - TwinConstraint::::NAME, - Bridge::::NAME, + Pesat::::NAME, + TwinConstraint::::NAME, + Bridge::::NAME, Ood::::NAME, SampleQueries::::NAME, Batching::::NAME, ], ); - let (l1_xs, parsed_acc) = parse_statement::( + let (l1_xs, parsed_acc) = parse_statement::( verifier_state, self.params.config.l1_first_fold_factor, self.params.config.l2_second_fold_factor, @@ -86,23 +84,23 @@ where .iter() .map(|p| p.x.clone()) .collect(); - let l2_roots = parsed_acc.rt_merkle_roots.clone(); + let l2_commitments = parsed_acc.rt_commitments.clone(); - let pesat_ior = Pesat:: { + let pesat_ior = Pesat:: { code: &self.params.code, - hasher: &self.params.hasher, + ck: &self.params.ck, _phantom: PhantomData, }; - let twin_constraint_ior = TwinConstraint:: { + let twin_constraint_ior = TwinConstraint:: { r1cs: self.params.predicate.constraints(), _phantom: PhantomData, }; - let bridge_ior = Bridge::::default(); + let bridge_ior = Bridge::::default(); let ood_ior = Ood::::default(); let sample_queries_ior = SampleQueries::::default(); let batching_ior = Batching::::default(); - let proximity_ior = Proximity:: { - hasher: &self.params.hasher, + let proximity_ior = Proximity:: { + ck: &self.params.ck, _phantom: PhantomData, }; @@ -113,7 +111,7 @@ where taus_zero_check_challenges, }, outputs: PesatVerifierOutputs { - rt_0_fresh_merkle_root, + rt_0_fresh_commitment, }, } = verify_ior!( pesat_ior, @@ -150,7 +148,7 @@ where BridgeReducedStatement { eta_predicate_eval: _, nu_0_oracle_eval, - td_new_root: _, + td_new_commitment: _, }, outputs: _, } = verify_ior!( @@ -168,9 +166,6 @@ where }, )?; - // Composition-safety assertion: any downstream IOR consuming TC's - // deferred check must have called discharge(). If this fires the - // protocol composition has a soundness gap. if !deferred.is_discharged() { return Err(VerifierError::Target); } @@ -202,9 +197,6 @@ where (proof.shift_query_answers.len() == self.params.config.t_num_queries) .then_some(()) .ok_or(VerifierError::NumShiftQueries)?; - (proof.auth_j.len() == self.params.config.l2_second_fold_factor) - .then_some(()) - .ok_or(VerifierError::NumL2Instances)?; let mut indexed: Vec<(usize, usize)> = queries .leaf_positions @@ -218,53 +210,27 @@ where let sorted_unique: Vec = indexed.iter().map(|&(p, _)| p).collect(); let row_indices: Vec = indexed.iter().map(|&(_, r)| r).collect(); + // Column tuples for the fresh PESAT commitment: per query, the + // l1 values from `shift_query_answers[row][l2..]`. let fresh_values: Vec> = row_indices .iter() .map(|&r| { proof.shift_query_answers[r][self.params.config.l2_second_fold_factor..].to_vec() }) .collect(); - let fresh_handle = MerkleIndexedOracle::new( - warp_scheme::(self.params.hasher.clone(), n_code_len), - &rt_0_fresh_merkle_root, - &proof.auth_0, - sorted_unique.clone(), - fresh_values, - ); - - let acc_handles: Vec> = - (0..self.params.config.l2_second_fold_factor) - .map(|j| { - let acc_values: Vec> = row_indices - .iter() - .map(|&r| vec![proof.shift_query_answers[r][j]]) - .collect(); - MerkleIndexedOracle::new( - warp_scheme::(self.params.hasher.clone(), n_code_len), - &l2_roots[j], - &proof.auth_j[j], - sorted_unique.clone(), - acc_values, - ) - }) - .collect(); - verify_ior!( - proximity_ior, - verifier_state, - statement: ProximityStatement { - queries: queries.clone(), - l2_second_fold_factor: self.params.config.l2_second_fold_factor, - t_num_queries: self.params.config.t_num_queries, - n_code_len, - }, - inputs: ProximityVerifierInputs { - fresh: &fresh_handle, - acc: &acc_handles, - _f: PhantomData, - }, - )?; + // Per-acc column tuples (each acc has m=1 so each tuple is length 1). + let acc_values: Vec>> = (0..self.params.config.l2_second_fold_factor) + .map(|j| { + row_indices + .iter() + .map(|&r| vec![proof.shift_query_answers[r][j]]) + .collect() + }) + .collect(); + // Batching consumes its transcript bytes BEFORE the proximity + // opens (matches the prover's order: ...→Batching→Proximity opens). let gamma_eq_evals = compute_hypercube_eq_evals(log_l, &gamma_sumcheck_challenges); let mut nu_i_oracle_evals = Vec::with_capacity( 1 + self.params.config.s_num_ood_samples + self.params.config.t_num_queries, @@ -302,6 +268,54 @@ where }, )?; + // Now consume opening proofs from the transcript (in the same + // order the prover wrote them: fresh first, then each acc). + let mut rng = OsRng; + V::check_multiple( + &self.params.vk, + &rt_0_fresh_commitment, + sorted_unique.iter().copied(), + fresh_values.iter().cloned(), + &mut rng, + verifier_state, + ) + .map_err(|_| VerifierError::ShiftQuery)?; + + for (j, commitment) in l2_commitments.iter().enumerate() { + V::check_multiple( + &self.params.vk, + commitment, + sorted_unique.iter().copied(), + acc_values[j].iter().cloned(), + &mut rng, + verifier_state, + ) + .map_err(|_| VerifierError::ShiftQuery)?; + } + + // Hand pre-validated lookup handles to the proximity IOR. + let fresh_handle = ValidatedOracle::new(sorted_unique.clone(), fresh_values); + let acc_handles: Vec> = acc_values + .into_iter() + .map(|vals| ValidatedOracle::new(sorted_unique.clone(), vals)) + .collect(); + + verify_ior!( + proximity_ior, + verifier_state, + statement: ProximityStatement { + queries: queries.clone(), + l2_second_fold_factor: self.params.config.l2_second_fold_factor, + t_num_queries: self.params.config.t_num_queries, + n_code_len, + }, + inputs: ProximityVerifierInputs { + fresh: &fresh_handle, + acc: &acc_handles, + _f: PhantomData, + }, + )?; + (acc_alpha_first == alpha_sumcheck_challenges) .then_some(()) .ok_or(VerifierError::CodeEvaluationPoint)?; diff --git a/tests/integration_warp.rs b/tests/integration_warp.rs index 2a7b1ef..8d3ab46 100644 --- a/tests/integration_warp.rs +++ b/tests/integration_warp.rs @@ -19,9 +19,13 @@ use ark_codes::{ }; use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; use ark_ff::UniformRand; -use ark_mt::blake3::Blake3FieldHasher; +use ark_mt::{ + blake3::Blake3FieldHasher, hash_region::HashRegion, scheme::MerkleCommitment, + shape::PerfectBinary, +}; use ark_serialize::{CanonicalSerialize, Compress}; use ark_std::rand::thread_rng; +use ark_vc::{mvc::MultiVectorCommitment, vc::VectorCommitment}; use warp::config::WARPConfig; use warp::relations::{ @@ -36,6 +40,29 @@ use warp::utils::poseidon; use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProverKey, WARPVerifierKey}; use warp::WARP; +/// Concrete Merkle scheme used by the tests below. Plain perfect-binary +/// tree with a Blake3 field-symbol hasher — no caps, no multi-region. +type MerkleVc = MerkleCommitment>, PerfectBinary>; + +fn build_keys( + code_len: usize, + num_queries: usize, +) -> ( + as VectorCommitment>::CommitterKey, + as VectorCommitment>::VerifierKey, +) +where + F: ark_ff::PrimeField, + Blake3FieldHasher: Default + Clone + 'static, +{ + let mut rng = thread_rng(); + let pp = + as MultiVectorCommitment>::setup_multiple(0, code_len, num_queries, &mut rng) + .expect("setup_multiple"); + as MultiVectorCommitment>::trim_multiple(&pp, 0, code_len, num_queries) + .expect("trim_multiple") +} + #[test] fn warp_test() { let l1 = 4; @@ -82,11 +109,13 @@ fn warp_test() { .unwrap(); let warp_config = WARPConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = WARP::, _, Blake3FieldHasher>::new( + let (ck, vk) = build_keys::(code.code_len(), t); + let hash_chain_warp = WARP::, _, MerkleVc>::new( warp_config.clone(), code.clone(), r1cs.clone(), - Blake3FieldHasher::::new(), + ck, + vk, ); let mut acc_x = AccumulatorInstance::empty(); @@ -118,11 +147,13 @@ fn warp_test() { let warp_config = WARPConfig::<_, R1CS>::new(l1, 4, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = WARP::, _, Blake3FieldHasher>::new( + let (ck, vk) = build_keys::(code.code_len(), t); + let hash_chain_warp = WARP::, _, MerkleVc>::new( warp_config.clone(), code.clone(), r1cs.clone(), - Blake3FieldHasher::::new(), + ck, + vk, ); let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); @@ -218,13 +249,14 @@ fn warp_test_goldilocks() { .unwrap(); let warp_config = WARPConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = - WARP::, _, Blake3FieldHasher>::new( - warp_config.clone(), - code.clone(), - r1cs.clone(), - Blake3FieldHasher::::new(), - ); + let (ck, vk) = build_keys::(code.code_len(), t); + let hash_chain_warp = WARP::, _, MerkleVc>::new( + warp_config.clone(), + code.clone(), + r1cs.clone(), + ck, + vk, + ); let mut acc_x = AccumulatorInstance::empty(); let mut acc_w = AccumulatorWitness::empty(); @@ -256,13 +288,14 @@ fn warp_test_goldilocks() { let warp_config = WARPConfig::<_, R1CS>::new(l1, 4, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = - WARP::, _, Blake3FieldHasher>::new( - warp_config.clone(), - code.clone(), - r1cs.clone(), - Blake3FieldHasher::::new(), - ); + let (ck, vk) = build_keys::(code.code_len(), t); + let hash_chain_warp = WARP::, _, MerkleVc>::new( + warp_config.clone(), + code.clone(), + r1cs.clone(), + ck, + vk, + ); let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), pf) = hash_chain_warp diff --git a/tests/verifier_negative.rs b/tests/verifier_negative.rs index 346256a..8504f13 100644 --- a/tests/verifier_negative.rs +++ b/tests/verifier_negative.rs @@ -26,9 +26,13 @@ use ark_codes::{ traits::LinearCode, }; use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; -use ark_mt::blake3::Blake3FieldHasher; +use ark_mt::{ + blake3::Blake3FieldHasher, hash_region::HashRegion, scheme::MerkleCommitment, + shape::PerfectBinary, +}; use ark_std::rand::thread_rng; use ark_std::UniformRand; +use ark_vc::{mvc::MultiVectorCommitment, vc::VectorCommitment}; use warp::config::WARPConfig; use warp::error::VerifierError; @@ -46,24 +50,38 @@ use warp::warp::{ use warp::WARP; type F = BLS12_381; -type H = Blake3FieldHasher; -type WarpT = WARP, ReedSolomon, H>; +type V = MerkleCommitment>, PerfectBinary>; +type WarpT = WARP, ReedSolomon, V>; + +fn build_keys( + code_len: usize, + num_queries: usize, +) -> ( + ::CommitterKey, + ::VerifierKey, +) { + let mut rng = thread_rng(); + let pp = ::setup_multiple(0, code_len, num_queries, &mut rng) + .expect("setup_multiple"); + ::trim_multiple(&pp, 0, code_len, num_queries) + .expect("trim_multiple") +} /// Everything the verifier needs to re-check, plus enough dimensions /// to re-derive the verifier state. struct Fixture { warp: WarpT, vk: WARPVerifierKey, - acc_x: AccumulatorInstance, - proof: WARPProof, + acc_x: AccumulatorInstance, + proof: WARPProof, narg_str: Vec, } impl Fixture { fn verify( &self, - acc_x: AccumulatorInstance, - proof: WARPProof, + acc_x: AccumulatorInstance, + proof: WARPProof, ) -> Result<(), VerifierError> { let domainsep_v = spongefish::domain_separator!("test::warp::negative"); let mut verifier_state = domainsep_v @@ -117,12 +135,8 @@ fn make_fixture() -> Fixture { // Phase 1: produce `l1` single-round acc states so we have a non-trivial // accumulator to feed phase 2 (l2 > 0 so NumL2Instances is reachable). let warp_cfg1 = WARPConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); - let w1 = WARP::, _, H>::new( - warp_cfg1, - code.clone(), - r1cs.clone(), - Blake3FieldHasher::::new(), - ); + let (ck1, vk1) = build_keys(code.code_len(), t); + let w1 = WARP::, _, V>::new(warp_cfg1, code.clone(), r1cs.clone(), ck1, vk1); let mut acc_x = AccumulatorInstance::empty(); let mut acc_w = AccumulatorWitness::empty(); @@ -151,8 +165,8 @@ fn make_fixture() -> Fixture { // Phase 2: the "real" prove with l2 > 0 accumulated instances. let warp_cfg2 = WARPConfig::<_, R1CS>::new(l1, 4, s, t, r1cs.config(), code.code_len()); - let warp = - WARP::, _, H>::new(warp_cfg2, code, r1cs.clone(), Blake3FieldHasher::::new()); + let (ck2, vk2) = build_keys(code.code_len(), t); + let warp = WARP::, _, V>::new(warp_cfg2, code, r1cs.clone(), ck2, vk2); let ds = spongefish::domain_separator!("test::warp::negative"); let mut ps = ds.without_session().instance(&0u32).std_prover(); @@ -242,23 +256,24 @@ fn truncated_shift_query_answers_raises_num_shift_queries() { } #[test] -fn tampered_shift_query_answer_raises_shift_query() { +fn tampered_shift_query_answer_raises_target() { let fix = make_fixture(); let mut proof = fix.proof.clone(); - // Each row of shift_query_answers has l2 + l1 entries; tampering any - // of them makes path.verify fail because the leaf hash no longer - // matches the committed root. + // Tampering a `shift_query_answer` is caught at two distinct points: + // (a) Batching's sumcheck consumes the answer to compute `nu_i` and + // its final-claim check fails (`Target`), and (b) the trait's + // `check_multiple` would also fail because the leaf no longer + // hashes to the committed root (`ShiftQuery`). With the verify + // ordering Batching → check_multiple, (a) fires first. proof.shift_query_answers[0][0] += F::from(1u64); - assert_err(fix.verify(fix.acc_x.clone(), proof), "ShiftQuery"); + assert_err(fix.verify(fix.acc_x.clone(), proof), "Target"); } -#[test] -fn truncated_auth_j_raises_num_l2_instances() { - let fix = make_fixture(); - let mut proof = fix.proof.clone(); - proof.auth_j.pop(); - assert_err(fix.verify(fix.acc_x.clone(), proof), "NumL2Instances"); -} +// `truncated_auth_j_raises_num_l2_instances` removed: opening proofs now +// live in the spongefish transcript (written by `V::open_multiple`), so +// there are no longer separate `auth_j` fields on `WARPProof` to truncate. +// Equivalent coverage would require transcript-byte tampering, which is a +// spongefish-layer concern, not warp's. #[test] fn tampered_mu_raises_target() { @@ -310,8 +325,8 @@ fn prove_rejects_mismatched_instance_witness_lengths() { .unzip(); let warp_cfg = WARPConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); - let warp = - WARP::, _, H>::new(warp_cfg, code, r1cs.clone(), Blake3FieldHasher::::new()); + let (ck, vk) = build_keys(code.code_len(), t); + let warp = WARP::, _, V>::new(warp_cfg, code, r1cs.clone(), ck, vk); let mut witnesses_short = witnesses; witnesses_short.pop(); From 9d40a97d69196915b2b76d14ff517b5cf6a618b7 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Wed, 13 May 2026 17:09:36 +0200 Subject: [PATCH 66/68] chkpt --- src/crypto/mod.rs | 2 +- src/crypto/{merkle/mod.rs => vc.rs} | 4 ++-- src/protocol/iors/bridge.rs | 2 +- src/protocol/iors/pesat.rs | 2 +- src/protocol/iors/proximity.rs | 7 +++---- src/protocol/oracles/{indexed_merkle.rs => indexed.rs} | 0 src/protocol/oracles/mod.rs | 2 +- src/warp/accumulator.rs | 2 +- src/warp/verify.rs | 2 +- 9 files changed, 11 insertions(+), 12 deletions(-) rename src/crypto/{merkle/mod.rs => vc.rs} (86%) rename src/protocol/oracles/{indexed_merkle.rs => indexed.rs} (100%) diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index bdf9eb8..22ea2ae 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1 +1 @@ -pub mod merkle; +pub mod vc; diff --git a/src/crypto/merkle/mod.rs b/src/crypto/vc.rs similarity index 86% rename from src/crypto/merkle/mod.rs rename to src/crypto/vc.rs index 7767d11..6d4ade8 100644 --- a/src/crypto/merkle/mod.rs +++ b/src/crypto/vc.rs @@ -3,8 +3,8 @@ //! Protocol code is generic over `ark_vc::mvc::MultiVectorCommitment`. //! This module's only job is the [`CommittedCodewords`] helper that //! pairs `(Commitment, CommitmentState)` from the trait with the -//! original column-codewords — the trait's `State` exposes leaves as -//! row-tuples, but warp threads codewords back for proximity opens +//! original column-codewords — `V::CommitmentState` is opaque per the +//! trait, so warp keeps codewords beside it for the proximity opens //! and the decider's recompute check. use ark_ff::Field; diff --git a/src/protocol/iors/bridge.rs b/src/protocol/iors/bridge.rs index 12bf430..494a049 100644 --- a/src/protocol/iors/bridge.rs +++ b/src/protocol/iors/bridge.rs @@ -8,7 +8,7 @@ use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState use std::marker::PhantomData; use crate::count_ops; -use crate::crypto::merkle::CommittedCodewords; +use crate::crypto::vc::CommittedCodewords; use crate::error::{ProverError, VerifierError}; use crate::protocol::ior::{ProverTriple, IOR}; use crate::protocol::iors::twin_constraint::DeferredOracleCheck; diff --git a/src/protocol/iors/pesat.rs b/src/protocol/iors/pesat.rs index c9235da..e376b0a 100644 --- a/src/protocol/iors/pesat.rs +++ b/src/protocol/iors/pesat.rs @@ -12,7 +12,7 @@ use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState use std::marker::PhantomData; use crate::count_ops; -use crate::crypto::merkle::CommittedCodewords; +use crate::crypto::vc::CommittedCodewords; use crate::error::VerifierError; use crate::protocol::ior::{ProverTriple, IOR}; diff --git a/src/protocol/iors/proximity.rs b/src/protocol/iors/proximity.rs index c3b9360..3e82d0f 100644 --- a/src/protocol/iors/proximity.rs +++ b/src/protocol/iors/proximity.rs @@ -12,10 +12,10 @@ use spongefish::{Encoding, NargDeserialize, NargSerialize, ProverState, Verifier use std::marker::PhantomData; use crate::count_ops; -use crate::crypto::merkle::CommittedCodewords; +use crate::crypto::vc::CommittedCodewords; use crate::error::VerifierError; use crate::protocol::ior::{ProverTriple, IOR}; -use crate::protocol::oracles::indexed_merkle::IndexedOracle; +use crate::protocol::oracles::indexed::IndexedOracle; use crate::protocol::oracles::query_indices::QueryIndices; pub struct ProximityStatement { @@ -89,8 +89,7 @@ where where Self: 'b; type VerifierInputs<'b> - = - ProximityVerifierInputs<'b, F, crate::protocol::oracles::indexed_merkle::ValidatedOracle> + = ProximityVerifierInputs<'b, F, crate::protocol::oracles::indexed::ValidatedOracle> where Self: 'b; type ReductionInputs = (); diff --git a/src/protocol/oracles/indexed_merkle.rs b/src/protocol/oracles/indexed.rs similarity index 100% rename from src/protocol/oracles/indexed_merkle.rs rename to src/protocol/oracles/indexed.rs diff --git a/src/protocol/oracles/mod.rs b/src/protocol/oracles/mod.rs index 914d31e..5b5936f 100644 --- a/src/protocol/oracles/mod.rs +++ b/src/protocol/oracles/mod.rs @@ -2,5 +2,5 @@ //! indexed-oracle handles, and verifier query positions. pub mod evaluation; -pub mod indexed_merkle; +pub mod indexed; pub mod query_indices; diff --git a/src/warp/accumulator.rs b/src/warp/accumulator.rs index 7204e0c..ecba80d 100644 --- a/src/warp/accumulator.rs +++ b/src/warp/accumulator.rs @@ -1,7 +1,7 @@ use ark_ff::Field; use ark_vc::mvc::MultiVectorCommitment; -use crate::crypto::merkle::CommittedCodewords; +use crate::crypto::vc::CommittedCodewords; /// Per-instance β coordinates absorbed into the accumulator: the `(τ, x)` /// twin pair. Replaces the old parallel-`Vec` tuple shape which let callers diff --git a/src/warp/verify.rs b/src/warp/verify.rs index 14dda1f..a0a5624 100644 --- a/src/warp/verify.rs +++ b/src/warp/verify.rs @@ -18,7 +18,7 @@ use crate::protocol::iors::{ sample_queries::{SampleQueries, SampleQueriesReducedStatement, SampleQueriesStatement}, twin_constraint::{TwinConstraint, TwinConstraintReducedStatement, TwinConstraintStatement}, }; -use crate::protocol::oracles::indexed_merkle::ValidatedOracle; +use crate::protocol::oracles::indexed::ValidatedOracle; use crate::protocol::transcript::parse_statement; use crate::relations::PolyPredicate; use crate::utils::{concat_slices, scale_and_sum}; From cf81504c4a46603eefc5b0e93a0f1998ac644796 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 14 May 2026 19:42:15 +0200 Subject: [PATCH 67/68] ark-iop --- Cargo.toml | 1 + benches/warp_rs.rs | 19 +- .../accumulator.rs | 0 .../decide.rs => accumulation_scheme/iop.rs} | 67 ++++++- src/{warp => accumulation_scheme}/keys.rs | 4 +- src/accumulation_scheme/mod.rs | 22 +++ src/{warp => accumulation_scheme}/params.rs | 6 +- src/{warp => accumulation_scheme}/proof.rs | 10 +- src/{warp => accumulation_scheme}/prove.rs | 67 +++---- src/{warp => accumulation_scheme}/scheme.rs | 26 +-- src/accumulation_scheme/trait_def.rs | 77 ++++++++ .../transcript.rs} | 70 ++++++- src/{warp => accumulation_scheme}/verify.rs | 63 +++---- src/bin/warp-params.rs | 2 +- src/config/mod.rs | 6 +- src/error.rs | 39 +++- src/{protocol => iop}/iors/batching.rs | 84 +++++---- src/{protocol => iop}/iors/bridge.rs | 102 ++++++---- src/{protocol => iop}/iors/mod.rs | 0 src/{protocol => iop}/iors/ood.rs | 64 +++++-- src/{protocol => iop}/iors/pesat.rs | 81 +++++--- src/{protocol => iop}/iors/proximity.rs | 117 ++++++------ src/{protocol => iop}/iors/sample_queries.rs | 72 +++---- src/{protocol => iop}/iors/twin_constraint.rs | 145 ++++++--------- src/iop/mod.rs | 2 + src/{protocol => iop}/oracles/evaluation.rs | 0 src/iop/oracles/indexed.rs | 5 + src/{protocol => iop}/oracles/mod.rs | 0 .../oracles/query_indices.rs | 12 +- src/lib.rs | 60 +++++- src/params/mod.rs | 2 +- src/protocol/ior.rs | 176 ------------------ src/protocol/mod.rs | 4 - src/protocol/oracles/indexed.rs | 41 ---- src/protocol/transcript/mod.rs | 31 --- src/protocol/transcript/prover.rs | 60 ------ src/relations/mod.rs | 6 +- src/relations/r1cs/hashchain/mod.rs | 4 +- src/relations/r1cs/mod.rs | 28 +-- src/serialize.rs | 8 +- src/warp/mod.rs | 16 -- tests/integration_warp.rs | 91 +++++---- tests/profile_json.rs | 23 +-- tests/snapshot_fs.rs | 150 +++++++++++++++ tests/verifier_negative.rs | 48 +++-- 45 files changed, 1058 insertions(+), 853 deletions(-) rename src/{warp => accumulation_scheme}/accumulator.rs (100%) rename src/{warp/decide.rs => accumulation_scheme/iop.rs} (50%) rename src/{warp => accumulation_scheme}/keys.rs (86%) create mode 100644 src/accumulation_scheme/mod.rs rename src/{warp => accumulation_scheme}/params.rs (82%) rename src/{warp => accumulation_scheme}/proof.rs (81%) rename src/{warp => accumulation_scheme}/prove.rs (90%) rename src/{warp => accumulation_scheme}/scheme.rs (75%) create mode 100644 src/accumulation_scheme/trait_def.rs rename src/{protocol/transcript/verifier.rs => accumulation_scheme/transcript.rs} (53%) rename src/{warp => accumulation_scheme}/verify.rs (90%) rename src/{protocol => iop}/iors/batching.rs (80%) rename src/{protocol => iop}/iors/bridge.rs (65%) rename src/{protocol => iop}/iors/mod.rs (100%) rename src/{protocol => iop}/iors/ood.rs (64%) rename src/{protocol => iop}/iors/pesat.rs (72%) rename src/{protocol => iop}/iors/proximity.rs (72%) rename src/{protocol => iop}/iors/sample_queries.rs (59%) rename src/{protocol => iop}/iors/twin_constraint.rs (73%) create mode 100644 src/iop/mod.rs rename src/{protocol => iop}/oracles/evaluation.rs (100%) create mode 100644 src/iop/oracles/indexed.rs rename src/{protocol => iop}/oracles/mod.rs (100%) rename src/{protocol => iop}/oracles/query_indices.rs (95%) delete mode 100644 src/protocol/ior.rs delete mode 100644 src/protocol/mod.rs delete mode 100644 src/protocol/oracles/indexed.rs delete mode 100644 src/protocol/transcript/mod.rs delete mode 100644 src/protocol/transcript/prover.rs delete mode 100644 src/warp/mod.rs create mode 100644 tests/snapshot_fs.rs diff --git a/Cargo.toml b/Cargo.toml index d6ffae8..865c194 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ tracing = { version = "0.1", default-features = false, features = ["std", "attri tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "env-filter", "registry"], optional = true } libc = { version = "0.2", optional = true } ark-codes = { git = "https://github.com/z-tech/ark-codes.git", branch = "z-tech/arkworks-0.6.0" } +ark-iop = { git = "https://github.com/arkworks-rs/z.git", branch = "z-tech/ark-iop" } ark-vc = { git = "https://github.com/arkworks-rs/ark-vc.git", branch = "z-tech/hash-regions" } ark-mt = { git = "https://github.com/arkworks-rs/ark-vc.git", branch = "z-tech/hash-regions", features = ["blake3", "blake3-field", "vc"] } diff --git a/benches/warp_rs.rs b/benches/warp_rs.rs index 80b971b..c8f9d56 100644 --- a/benches/warp_rs.rs +++ b/benches/warp_rs.rs @@ -11,9 +11,9 @@ use ark_vc::mvc::MultiVectorCommitment; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use utils::domainsep::init_prover_state; use utils::hash_chain::{get_hashchain_instance_witness_pairs, get_hashchain_r1cs}; -use warp::config::WARPConfig; -use warp::warp::WARPProverKey; -use warp::WARP; +use warp::config::WarpConfig; +use warp::warp::WarpProverKey; +use warp::WarpAccumulationScheme; mod utils; use utils::poseidon; @@ -39,14 +39,19 @@ pub fn bench_rs_warp_fields(c: &mut Criterion) { type V = MerkleCommitment>, PerfectBinary>; for l in [32, 64, 128, 256, 512] { - let warp_config = WARPConfig::new(l, 0, s, t, r1cs.config(), code.code_len()); + let warp_config = WarpConfig::new(l, 0, s, t, r1cs.config(), code.code_len()); let pp = ::setup_multiple(0, code.code_len(), t, &mut rng) .expect("setup_multiple"); let (ck, vk) = ::trim_multiple(&pp, 0, code.code_len(), t) .expect("trim_multiple"); - let hash_chain_warp = - WARP::<_, _, _, V>::new(warp_config.clone(), code.clone(), r1cs.clone(), ck, vk); + let hash_chain_warp = WarpAccumulationScheme::<_, _, _, V>::new( + warp_config.clone(), + code.clone(), + r1cs.clone(), + ck, + vk, + ); let instances_witnesses = get_hashchain_instance_witness_pairs(l, &poseidon_config, HASHCHAIN_SIZE, &mut rng); @@ -65,7 +70,7 @@ pub fn bench_rs_warp_fields(c: &mut Criterion) { |(mut prover_state, _x_w)| { let _ = hash_chain_warp .prove( - WARPProverKey { + WarpProverKey { index: r1cs.clone(), m_num_constraints: r1cs.m_num_constraints, n_num_variables: r1cs.n_num_variables, diff --git a/src/warp/accumulator.rs b/src/accumulation_scheme/accumulator.rs similarity index 100% rename from src/warp/accumulator.rs rename to src/accumulation_scheme/accumulator.rs diff --git a/src/warp/decide.rs b/src/accumulation_scheme/iop.rs similarity index 50% rename from src/warp/decide.rs rename to src/accumulation_scheme/iop.rs index 4fc0205..611ad1d 100644 --- a/src/warp/decide.rs +++ b/src/accumulation_scheme/iop.rs @@ -1,5 +1,12 @@ +//! WarpAccumulationScheme's trait impls: `IOP` (per-round protocol identity) and +//! `AccumulationScheme` (split-accumulation wrapper). Both are +//! implemented directly on `WarpAccumulationScheme` — no phantom markers. +//! The same value that holds params (code, ck, predicate) is also +//! the FS-prologue absorber, the IOR-list owner, and the decider. + use ark_codes::traits::LinearCode; use ark_ff::{Field, PrimeField}; +use ark_iop::{IOP, IOR}; use ark_poly::{DenseMultilinearExtension, Polynomial}; use ark_std::log2; use ark_vc::mvc::MultiVectorCommitment; @@ -7,24 +14,66 @@ use ark_vc::vc::VectorCommitment; use effsc::hypercube::compute_hypercube_eq_evals; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize}; -use crate::error::{DeciderError, WARPError}; +use crate::accumulation_scheme::AccumulationScheme; +use crate::accumulation_scheme::{ + accumulator::{AccumulatorInstance, AccumulatorWitness}, + proof::WarpProof, + scheme::WarpAccumulationScheme, +}; +use crate::error::DeciderError; +use crate::iop::iors::{ + batching::Batching, bridge::Bridge, ood::Ood, pesat::Pesat, sample_queries::SampleQueries, + twin_constraint::TwinConstraint, +}; use crate::relations::PolyPredicate; -use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; -use crate::warp::scheme::WARP; -impl WARP +impl IOP for WarpAccumulationScheme where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, P: Clone + PolyPredicate, C: LinearCode + Clone, V: MultiVectorCommitment, - V::Commitment: Clone + Eq, + V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize + Clone, { - pub fn decide( + const NAME: &'static str = "WARP"; + + fn ior_names() -> Vec<&'static str> { + vec![ + as IOR>::NAME, + as IOR>::NAME, + as IOR>::NAME, + as IOR>::NAME, + as IOR>::NAME, + as IOR>::NAME, + ] + } + + type Statement = Vec>; + type Witness = Vec>; + type Proof = WarpProof; +} + +impl AccumulationScheme for WarpAccumulationScheme +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + P: Clone + PolyPredicate, + C: LinearCode + Clone, + V: MultiVectorCommitment, + V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, +{ + const NAME: &'static str = "WARP-AccScheme"; + type Iop = Self; + type FreshInstance = Vec>; + type FreshWitness = Vec>; + type AccumulatorInstance = AccumulatorInstance; + type AccumulatorWitness = AccumulatorWitness; + type AccumulationProof = WarpProof; + + fn decide( &self, - acc_witness: AccumulatorWitness, - acc_instance: AccumulatorInstance, - ) -> Result<(), WARPError> { + acc_instance: &Self::AccumulatorInstance, + acc_witness: &Self::AccumulatorWitness, + ) -> Result<(), DeciderError> { let acc_codeword = &acc_witness.td_committed_codewords[0].codewords[0]; let computed_f = self.params.code.encode(&acc_witness.w_witnesses[0]); diff --git a/src/warp/keys.rs b/src/accumulation_scheme/keys.rs similarity index 86% rename from src/warp/keys.rs rename to src/accumulation_scheme/keys.rs index 2e095cf..aad984a 100644 --- a/src/warp/keys.rs +++ b/src/accumulation_scheme/keys.rs @@ -1,6 +1,6 @@ /// Prover key — relation index plus dimensions `(M, N, k)`. #[derive(Clone)] -pub struct WARPProverKey

    { +pub struct WarpProverKey

    { pub index: P, pub m_num_constraints: usize, pub n_num_variables: usize, @@ -9,7 +9,7 @@ pub struct WARPProverKey

    { /// Verifier key — dimensions only `(M, N, k)`. #[derive(Clone, Copy)] -pub struct WARPVerifierKey { +pub struct WarpVerifierKey { pub m_num_constraints: usize, pub n_num_variables: usize, pub k_num_witness_vars: usize, diff --git a/src/accumulation_scheme/mod.rs b/src/accumulation_scheme/mod.rs new file mode 100644 index 0000000..8f1fd33 --- /dev/null +++ b/src/accumulation_scheme/mod.rs @@ -0,0 +1,22 @@ +//! WarpAccumulationScheme accumulation scheme: the `AccumulationScheme` trait (in [`trait_def`]) +//! and WarpAccumulationScheme's implementation of it (orchestration in [`prove`] / [`verify`], +//! decider + IOP/AccumulationScheme trait impls in [`iop`], plus types +//! [`scheme`], [`accumulator`], [`keys`], [`params`], [`proof`]). + +pub mod accumulator; +pub mod iop; +pub mod keys; +pub mod params; +pub mod proof; +pub mod prove; +pub mod scheme; +pub mod trait_def; +pub mod transcript; +pub mod verify; + +pub use accumulator::{AccumulatorInstance, AccumulatorWitness}; +pub use keys::{WarpProverKey, WarpVerifierKey}; +pub use params::WarpParams; +pub use proof::{ProveResult, WarpProof}; +pub use scheme::WarpAccumulationScheme; +pub use trait_def::AccumulationScheme; diff --git a/src/warp/params.rs b/src/accumulation_scheme/params.rs similarity index 82% rename from src/warp/params.rs rename to src/accumulation_scheme/params.rs index 399a336..2d897e8 100644 --- a/src/warp/params.rs +++ b/src/accumulation_scheme/params.rs @@ -3,11 +3,11 @@ use ark_ff::Field; use ark_vc::mvc::MultiVectorCommitment; use std::marker::PhantomData; -use crate::config::WARPConfig; +use crate::config::WarpConfig; use crate::relations::PolyPredicate; /// Shared configuration used by all IORs. -pub struct WARPParams +pub struct WarpParams where F: Field, P: PolyPredicate, @@ -15,7 +15,7 @@ where V: MultiVectorCommitment, { pub(crate) _phantom_f: PhantomData, - pub config: WARPConfig, + pub config: WarpConfig, pub code: C, pub predicate: P, pub ck: V::CommitterKey, diff --git a/src/warp/proof.rs b/src/accumulation_scheme/proof.rs similarity index 81% rename from src/warp/proof.rs rename to src/accumulation_scheme/proof.rs index 94c5af8..9aab8a9 100644 --- a/src/warp/proof.rs +++ b/src/accumulation_scheme/proof.rs @@ -1,14 +1,14 @@ use ark_ff::Field; use ark_vc::mvc::MultiVectorCommitment; +use crate::accumulation_scheme::accumulator::{AccumulatorInstance, AccumulatorWitness}; use crate::error::ProverError; -use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness}; -/// Proof produced by the WARP accumulation prover. Auth paths and +/// Proof produced by the WarpAccumulationScheme accumulation prover. Auth paths and /// sibling digests now live in the spongefish transcript (the trait's /// `open_multiple` writes them via `prover_state.prover_message`), so /// they no longer appear here as separate fields. -pub struct WARPProof +pub struct WarpProof where F: Field, V: MultiVectorCommitment, @@ -20,7 +20,7 @@ where pub shift_query_answers: Vec>, } -impl Clone for WARPProof +impl Clone for WarpProof where F: Field, V: MultiVectorCommitment, @@ -40,7 +40,7 @@ where pub type ProveResult = Result< ( (AccumulatorInstance, AccumulatorWitness), - WARPProof, + WarpProof, ), ProverError, >; diff --git a/src/warp/prove.rs b/src/accumulation_scheme/prove.rs similarity index 90% rename from src/warp/prove.rs rename to src/accumulation_scheme/prove.rs index c30492b..c643fc6 100644 --- a/src/warp/prove.rs +++ b/src/accumulation_scheme/prove.rs @@ -5,9 +5,16 @@ use ark_vc::mvc::MultiVectorCommitment; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState}; use std::marker::PhantomData; +use crate::accumulation_scheme::accumulator::{ + AccumulatorInstance, AccumulatorWitness, BetaTwinPair, +}; +use crate::accumulation_scheme::keys::WarpProverKey; +use crate::accumulation_scheme::proof::{ProveResult, WarpProof}; +use crate::accumulation_scheme::scheme::WarpAccumulationScheme; +use crate::accumulation_scheme::transcript::absorb_instances; +use crate::accumulation_scheme::AccumulationScheme; use crate::error::ProverError; -use crate::protocol::ior::{absorb_protocol_map_prover, IorProveResult, IOR}; -use crate::protocol::iors::{ +use crate::iop::iors::{ batching::{ Batching, BatchingProverInputs, BatchingReducedStatement, BatchingReducedWitness, BatchingStatement, @@ -25,25 +32,19 @@ use crate::protocol::iors::{ TwinConstraintReducedWitness, TwinConstraintStatement, TwinConstraintWitness, }, }; -use crate::protocol::transcript::absorb_instances; -use crate::prove_ior; use crate::relations::PolyPredicate; -use crate::warp::accumulator::{AccumulatorInstance, AccumulatorWitness, BetaTwinPair}; -use crate::warp::keys::WARPProverKey; -use crate::warp::proof::{ProveResult, WARPProof}; -use crate::warp::scheme::WARP; -impl WARP +impl WarpAccumulationScheme where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, P: Clone + PolyPredicate, C: LinearCode + Clone, V: MultiVectorCommitment, - V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize + Clone, + V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, { fn validate_prover_inputs( &self, - pk: &WARPProverKey

    , + pk: &WarpProverKey

    , instances: &[Vec], witnesses: &[Vec], acc_instance: &AccumulatorInstance, @@ -103,7 +104,7 @@ where #[tracing::instrument(name = "warp.prove", skip_all)] pub fn prove( &self, - pk: WARPProverKey

    , + pk: WarpProverKey

    , prover_state: &mut ProverState, witnesses: Vec>, instances: Vec>, @@ -118,17 +119,7 @@ where let log_n = log2(n_code_len) as usize; let n_minus_k = pk.n_num_variables - pk.k_num_witness_vars; - absorb_protocol_map_prover( - prover_state, - &[ - Pesat::::NAME, - TwinConstraint::::NAME, - Bridge::::NAME, - Ood::::NAME, - SampleQueries::::NAME, - Batching::::NAME, - ], - ); + self.absorb_scheme_prologue_prover(prover_state); absorb_instances(prover_state, &instances); acc_instance.absorb_into(prover_state); @@ -157,7 +148,7 @@ where _phantom: PhantomData, }; - let IorProveResult { + let ark_iop::IorProveResult { reduced: PesatReducedStatement { mus_codeword_first_coords, @@ -169,7 +160,7 @@ where codewords, td_0_committed_codeword, }, - } = prove_ior!( + } = ark_iop::prove_ior!( pesat_ior, prover_state, statement: PesatStatement { @@ -180,7 +171,7 @@ where inputs: (), )?; - let IorProveResult { + let ark_iop::IorProveResult { reduced: TwinConstraintReducedStatement { gamma_sumcheck_challenges: _, @@ -194,7 +185,7 @@ where f_oracle, z_witness_assignment, }, - } = prove_ior!( + } = ark_iop::prove_ior!( twin_constraint_ior, prover_state, statement: TwinConstraintStatement { @@ -216,7 +207,7 @@ where }, )?; - let IorProveResult { + let ark_iop::IorProveResult { reduced: BridgeReducedStatement { eta_predicate_eval, @@ -230,7 +221,7 @@ where new_x, new_w, }, - } = prove_ior!( + } = ark_iop::prove_ior!( bridge_ior, prover_state, statement: BridgeStatement { @@ -250,7 +241,7 @@ where }, )?; - let IorProveResult { + let ark_iop::IorProveResult { reduced: OodReducedStatement { samples_flat, @@ -258,7 +249,7 @@ where }, proof: _, witness: _, - } = prove_ior!( + } = ark_iop::prove_ior!( ood_ior, prover_state, statement: OodStatement { s_num_ood_samples: self.params.config.s_num_ood_samples, log_n }, @@ -266,11 +257,11 @@ where inputs: OodProverInputs { oracle: &f_oracle }, )?; - let IorProveResult { + let ark_iop::IorProveResult { reduced: SampleQueriesReducedStatement { queries }, proof: _, witness: _, - } = prove_ior!( + } = ark_iop::prove_ior!( sample_queries_ior, prover_state, statement: SampleQueriesStatement { log_n, t_num_queries: self.params.config.t_num_queries }, @@ -278,14 +269,14 @@ where inputs: (), )?; - let IorProveResult { + let ark_iop::IorProveResult { reduced: BatchingReducedStatement { alpha_sumcheck_challenges, }, proof: _, witness: BatchingReducedWitness { mu_claimed_eval }, - } = prove_ior!( + } = ark_iop::prove_ior!( batching_ior, prover_state, statement: BatchingStatement::from_ior_outputs( @@ -300,13 +291,13 @@ where inputs: BatchingProverInputs { oracle: &f_oracle }, )?; - let IorProveResult { + let ark_iop::IorProveResult { reduced: _, proof: ProximityProofString { shift_query_answers, }, witness: _, - } = prove_ior!( + } = ark_iop::prove_ior!( proximity_ior, prover_state, statement: ProximityStatement { @@ -341,7 +332,7 @@ where td_committed_codewords: vec![td_new], w_witnesses: vec![new_w], }; - let proof = WARPProof { + let proof = WarpProof { rt_0_fresh_commitment: td_0_committed_codeword.commitment.clone(), mu_i_first_codeword_coords: mus_codeword_first_coords, nu_0_oracle_eval, diff --git a/src/warp/scheme.rs b/src/accumulation_scheme/scheme.rs similarity index 75% rename from src/warp/scheme.rs rename to src/accumulation_scheme/scheme.rs index 97753f4..86c12b3 100644 --- a/src/warp/scheme.rs +++ b/src/accumulation_scheme/scheme.rs @@ -6,22 +6,22 @@ use spongefish::{ }; use std::marker::PhantomData; -use crate::config::WARPConfig; +use crate::accumulation_scheme::keys::{WarpProverKey, WarpVerifierKey}; +use crate::accumulation_scheme::params::WarpParams; +use crate::config::WarpConfig; use crate::relations::PolyPredicate; -use crate::warp::keys::{WARPProverKey, WARPVerifierKey}; -use crate::warp::params::WARPParams; -pub struct WARP +pub struct WarpAccumulationScheme where F: Field, P: PolyPredicate, C: LinearCode + Clone, V: MultiVectorCommitment, { - pub params: WARPParams, + pub params: WarpParams, } -impl WARP +impl WarpAccumulationScheme where F: Field, P: Clone + PolyPredicate, @@ -29,14 +29,14 @@ where V: MultiVectorCommitment, { pub fn new( - config: WARPConfig, + config: WarpConfig, code: C, predicate: P, ck: V::CommitterKey, vk: V::VerifierKey, - ) -> WARP { + ) -> WarpAccumulationScheme { Self { - params: WARPParams { + params: WarpParams { _phantom_f: PhantomData, config, code, @@ -48,7 +48,7 @@ where } } -impl WARP +impl WarpAccumulationScheme where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, P: Clone + PolyPredicate, @@ -58,20 +58,20 @@ where pub fn index( prover_state: &mut ProverState, index: P, - ) -> VerificationResult<(WARPProverKey

    , WARPVerifierKey)> { + ) -> VerificationResult<(WarpProverKey

    , WarpVerifierKey)> { let (m, n, k) = index.config(); prover_state.public_message(&index.description()); prover_state.prover_message(&F::from(m as u32)); prover_state.prover_message(&F::from(n as u32)); prover_state.prover_message(&F::from(k as u32)); Ok(( - WARPProverKey { + WarpProverKey { index, m_num_constraints: m, n_num_variables: n, k_num_witness_vars: k, }, - WARPVerifierKey { + WarpVerifierKey { m_num_constraints: m, n_num_variables: n, k_num_witness_vars: k, diff --git a/src/accumulation_scheme/trait_def.rs b/src/accumulation_scheme/trait_def.rs new file mode 100644 index 0000000..a990da1 --- /dev/null +++ b/src/accumulation_scheme/trait_def.rs @@ -0,0 +1,77 @@ +//! Accumulation-scheme trait — warp-local, slated for upstream to +//! `ark-iop` once a second accumulating consumer appears. +//! +//! Mirrors the BCLMS (Bünz–Chen–Lin–Mishra–Vesely) split-accumulation +//! shape: an accumulator splits into a public `Instance` and a private +//! `Witness`; the scheme accumulates fresh claims via an inner IOP run +//! per round; the decider closes the accumulator. Atomic accumulation +//! falls out as `AccumulatorWitness = ()` and `AccumulationProof = ()`. +//! +//! Identity-shaped trait (no method signatures). The scheme is the FS +//! root once it implements this — its prologue absorbs `AS:NAME` plus +//! the inner IOP's name and IOR list, so callers no longer also need +//! to invoke `IOP::absorb_protocol_map_*`. + +use ark_iop::{ProverTranscript, VerifierTranscript, IOP}; + +use crate::error::DeciderError; + +pub trait AccumulationScheme { + const NAME: &'static str; + + /// The IOP whose transcripts this scheme accumulates per round. + type Iop: IOP; + + /// One fresh claim's public part. Often equal to `::Statement`; kept separate to allow batching or + /// transcript-derived forms. + type FreshInstance; + /// Witness counterpart of `FreshInstance`. + type FreshWitness; + + /// Public part of the accumulator (BCLMS "instance"). + type AccumulatorInstance; + /// Private part of the accumulator (BCLMS "witness"). Atomic + /// schemes set this to `()`. + type AccumulatorWitness; + + /// Per-round proof of correct accumulation. For atomic schemes + /// this is typically `()` (fold happens in-line). + type AccumulationProof; + + /// Absorb the scheme-level FS prologue: `AS:` tag + scheme NAME + + /// inner IOP NAME + inner IOP ior_names. Subsumes the inner + /// `IOP::absorb_protocol_map_prover` — callers invoke one or the + /// other, not both. + fn absorb_scheme_prologue_prover(&self, transcript: &mut P) { + transcript.public_message(b"AS:"); + transcript.public_message(Self::NAME.as_bytes()); + transcript.public_message(b"|"); + transcript.public_message(::NAME.as_bytes()); + transcript.public_message(b"|"); + for name in ::ior_names() { + transcript.public_message(name.as_bytes()); + transcript.public_message(b"|"); + } + } + + fn absorb_scheme_prologue_verifier(&self, transcript: &mut V) { + transcript.public_message(b"AS:"); + transcript.public_message(Self::NAME.as_bytes()); + transcript.public_message(b"|"); + transcript.public_message(::NAME.as_bytes()); + transcript.public_message(b"|"); + for name in ::ior_names() { + transcript.public_message(name.as_bytes()); + transcript.public_message(b"|"); + } + } + + /// Decider — closes the accumulator. The single BCLMS-uniform check + /// across split-accumulation schemes: `(instance, witness) → ok/err`. + fn decide( + &self, + acc_instance: &Self::AccumulatorInstance, + acc_witness: &Self::AccumulatorWitness, + ) -> Result<(), DeciderError>; +} diff --git a/src/protocol/transcript/verifier.rs b/src/accumulation_scheme/transcript.rs similarity index 53% rename from src/protocol/transcript/verifier.rs rename to src/accumulation_scheme/transcript.rs index 1d91489..7be4d2c 100644 --- a/src/protocol/transcript/verifier.rs +++ b/src/accumulation_scheme/transcript.rs @@ -1,13 +1,28 @@ +//! Warp-specific transcript helpers: `AccumulatorInstance` (de)serialization +//! plus the plain-instance absorber. + use ark_ff::Field; use ark_vc::mvc::MultiVectorCommitment; -use spongefish::{Decoding, Encoding, NargDeserialize, VerificationResult, VerifierState}; +use spongefish::{ + Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerificationResult, + VerifierState, +}; -use crate::warp::AccumulatorInstance; +use crate::accumulation_scheme::{accumulator::BetaTwinPair, AccumulatorInstance}; -// (l1 instances, accumulated instance) pub type ParsedStatement = (Vec>, AccumulatorInstance); -// parse l1 plain instances + an AccumulatorInstance from the transcript +pub fn absorb_instances>( + prover_state: &mut ProverState, + instances: &[Vec], +) { + for instance in instances { + for f in instance { + prover_state.prover_message(f); + } + } +} + pub fn parse_statement( verifier_state: &mut VerifierState<'_>, l1: usize, @@ -31,7 +46,48 @@ where Ok((l1_xs, acc)) } -// parse an AccumulatorInstance from the verifier transcript +impl AccumulatorInstance +where + F: Field + Encoding<[u8]>, + V: MultiVectorCommitment, + V::Commitment: Encoding<[u8]> + NargSerialize, +{ + pub fn absorb_into(&self, prover_state: &mut ProverState) { + for commitment in &self.rt_commitments { + prover_state.prover_message(commitment); + } + + for alpha in &self.alpha_fold_vectors { + for f in alpha { + prover_state.prover_message(f); + } + } + + for f in &self.mu_claimed_evals { + prover_state.prover_message(f); + } + + // Layout: all τ vectors first (l2 of them), then all x vectors. + // Verifier reads in the same order. Keeping the τ/x halves in + // separate passes lets the verifier reconstruct the pair list + // without needing length-prefixes per pair. + for pair in &self.beta_twin_pairs { + for f in &pair.tau { + prover_state.prover_message(f); + } + } + for pair in &self.beta_twin_pairs { + for f in &pair.x { + prover_state.prover_message(f); + } + } + + for f in &self.eta_predicate_evals { + prover_state.prover_message(f); + } + } +} + impl AccumulatorInstance where F: Field + NargDeserialize + Encoding<[u8]> + Decoding<[u8]>, @@ -63,10 +119,10 @@ where .map(|_| verifier_state.prover_messages_vec(instance_len)) .collect::>()?; - let beta_twin_pairs: Vec> = taus + let beta_twin_pairs: Vec> = taus .into_iter() .zip(xs) - .map(|(tau, x)| crate::warp::accumulator::BetaTwinPair { tau, x }) + .map(|(tau, x)| BetaTwinPair { tau, x }) .collect(); let eta: Vec = verifier_state.prover_messages_vec(l2)?; diff --git a/src/warp/verify.rs b/src/accumulation_scheme/verify.rs similarity index 90% rename from src/warp/verify.rs rename to src/accumulation_scheme/verify.rs index a0a5624..1140423 100644 --- a/src/warp/verify.rs +++ b/src/accumulation_scheme/verify.rs @@ -7,9 +7,14 @@ use rand_core::OsRng; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, VerifierState}; use std::marker::PhantomData; +use crate::accumulation_scheme::accumulator::AccumulatorInstance; +use crate::accumulation_scheme::keys::WarpVerifierKey; +use crate::accumulation_scheme::proof::WarpProof; +use crate::accumulation_scheme::scheme::WarpAccumulationScheme; +use crate::accumulation_scheme::transcript::parse_statement; +use crate::accumulation_scheme::AccumulationScheme; use crate::error::VerifierError; -use crate::protocol::ior::{absorb_protocol_map_verifier, IorVerifyResult, IOR}; -use crate::protocol::iors::{ +use crate::iop::iors::{ batching::{Batching, BatchingReducedStatement, BatchingStatement, BatchingVerifierInputs}, bridge::{Bridge, BridgeReducedStatement, BridgeStatement, BridgeVerifierInputs}, ood::{Ood, OodReducedStatement, OodStatement}, @@ -18,30 +23,24 @@ use crate::protocol::iors::{ sample_queries::{SampleQueries, SampleQueriesReducedStatement, SampleQueriesStatement}, twin_constraint::{TwinConstraint, TwinConstraintReducedStatement, TwinConstraintStatement}, }; -use crate::protocol::oracles::indexed::ValidatedOracle; -use crate::protocol::transcript::parse_statement; +use crate::iop::oracles::indexed::ValidatedOracle; use crate::relations::PolyPredicate; use crate::utils::{concat_slices, scale_and_sum}; -use crate::verify_ior; -use crate::warp::accumulator::AccumulatorInstance; -use crate::warp::keys::WARPVerifierKey; -use crate::warp::proof::WARPProof; -use crate::warp::scheme::WARP; -impl WARP +impl WarpAccumulationScheme where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, P: Clone + PolyPredicate, C: LinearCode + Clone, V: MultiVectorCommitment, - V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize + Clone, + V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize + Clone + Eq, { pub fn verify<'a>( &self, - vk: WARPVerifierKey, + vk: WarpVerifierKey, verifier_state: &mut VerifierState<'a>, acc_instance: AccumulatorInstance, - proof: WARPProof, + proof: WarpProof, ) -> Result<(), VerifierError> { let log_l = log2(self.params.config.l_total_fold_factor()) as usize; let log_m = log2(vk.m_num_constraints) as usize; @@ -49,17 +48,7 @@ where let log_n = log2(n_code_len) as usize; let n_minus_k = vk.n_num_variables - vk.k_num_witness_vars; - absorb_protocol_map_verifier( - verifier_state, - &[ - Pesat::::NAME, - TwinConstraint::::NAME, - Bridge::::NAME, - Ood::::NAME, - SampleQueries::::NAME, - Batching::::NAME, - ], - ); + self.absorb_scheme_prologue_verifier(verifier_state); let (l1_xs, parsed_acc) = parse_statement::( verifier_state, @@ -104,7 +93,7 @@ where _phantom: PhantomData, }; - let IorVerifyResult { + let ark_iop::IorVerifyResult { reduced: PesatReducedStatement { mus_codeword_first_coords, @@ -113,14 +102,14 @@ where outputs: PesatVerifierOutputs { rt_0_fresh_commitment, }, - } = verify_ior!( + } = ark_iop::verify_ior!( pesat_ior, verifier_state, statement: PesatStatement { l1_first_fold_factor: self.params.config.l1_first_fold_factor, log_m }, inputs: (), )?; - let IorVerifyResult { + let ark_iop::IorVerifyResult { reduced: TwinConstraintReducedStatement { gamma_sumcheck_challenges, @@ -129,7 +118,7 @@ where deferred, }, outputs: _, - } = verify_ior!( + } = ark_iop::verify_ior!( twin_constraint_ior, verifier_state, statement: TwinConstraintStatement { @@ -143,7 +132,7 @@ where inputs: (), )?; - let IorVerifyResult { + let ark_iop::IorVerifyResult { reduced: BridgeReducedStatement { eta_predicate_eval: _, @@ -151,7 +140,7 @@ where td_new_commitment: _, }, outputs: _, - } = verify_ior!( + } = ark_iop::verify_ior!( bridge_ior, verifier_state, statement: BridgeStatement { @@ -170,24 +159,24 @@ where return Err(VerifierError::Target); } - let IorVerifyResult { + let ark_iop::IorVerifyResult { reduced: OodReducedStatement { samples_flat, answers, }, outputs: _, - } = verify_ior!( + } = ark_iop::verify_ior!( ood_ior, verifier_state, statement: OodStatement { s_num_ood_samples: self.params.config.s_num_ood_samples, log_n }, inputs: (), )?; - let IorVerifyResult { + let ark_iop::IorVerifyResult { reduced: SampleQueriesReducedStatement { queries }, outputs: _, - } = verify_ior!( + } = ark_iop::verify_ior!( sample_queries_ior, verifier_state, statement: SampleQueriesStatement { log_n, t_num_queries: self.params.config.t_num_queries }, @@ -245,13 +234,13 @@ where nu_i_oracle_evals.push(nu_st); } - let IorVerifyResult { + let ark_iop::IorVerifyResult { reduced: BatchingReducedStatement { alpha_sumcheck_challenges, }, outputs: _, - } = verify_ior!( + } = ark_iop::verify_ior!( batching_ior, verifier_state, statement: BatchingStatement::from_ior_outputs( @@ -300,7 +289,7 @@ where .map(|vals| ValidatedOracle::new(sorted_unique.clone(), vals)) .collect(); - verify_ior!( + ark_iop::verify_ior!( proximity_ior, verifier_state, statement: ProximityStatement { diff --git a/src/bin/warp-params.rs b/src/bin/warp-params.rs index b305df8..9479882 100644 --- a/src/bin/warp-params.rs +++ b/src/bin/warp-params.rs @@ -25,7 +25,7 @@ fn main() -> ExitCode { fn print_usage() { eprintln!( - "warp-params — pick soundness-driven WARP parameters.\n\ + "warp-params — pick soundness-driven WarpAccumulationScheme parameters.\n\ \n\ Usage:\n\ warp-params select --lambda N --rate NUM/DEN|FLOAT --field-bits N --regime provable|conjectured\n\ diff --git a/src/config/mod.rs b/src/config/mod.rs index 59ef2d1..e1c6e8a 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -4,7 +4,7 @@ use crate::params::{validate, ParamError, Params, Regime, SecurityLevel, Soundne use crate::relations::PolyPredicate; #[derive(Clone)] -pub struct WARPConfig> { +pub struct WarpConfig> { pub l1_first_fold_factor: usize, pub l2_second_fold_factor: usize, pub s_num_ood_samples: usize, @@ -13,7 +13,7 @@ pub struct WARPConfig> { pub n_code_len: usize, } -impl> WARPConfig { +impl> WarpConfig { pub fn new( l1_first_fold_factor: usize, l2_second_fold_factor: usize, @@ -39,7 +39,7 @@ impl> WARPConfig { /// Fail-closed soundness check on `(s, t)`. Returns `Ok(bound)` only when /// every admissibility flag passes and proximity soundness meets the - /// target. `WARPConfig::new` itself does not call this (it cannot, the + /// target. `WarpConfig::new` itself does not call this (it cannot, the /// security target / field-size / rate / regime live outside the config); /// callers building a config for production use **must** call this before /// invoking the prover. See `docs/paper-mods/mod4_parameter_selection.tex`. diff --git a/src/error.rs b/src/error.rs index 2c28004..59d7387 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,7 +2,7 @@ use ark_crypto_primitives::Error; use thiserror::Error; #[derive(Error, Debug)] -pub enum WARPError { +pub enum WarpError { #[error(transparent)] ProverError(#[from] ProverError), #[error(transparent)] @@ -51,6 +51,24 @@ impl From for ProverError { } } +impl From for ProverError { + fn from(e: ark_iop::IorProverError) -> Self { + match e { + ark_iop::IorProverError::StatementShape { + what, + expected, + got, + } => Self::StatementShape { + what, + expected, + got, + }, + ark_iop::IorProverError::Transcript(_) => Self::SpongeFish, + ark_iop::IorProverError::Custom(msg) => Self::ConfigParameterInvalid { reason: msg }, + } + } +} + #[derive(Error, Debug)] pub enum VerifierError { #[error(transparent)] @@ -89,6 +107,25 @@ impl From for VerifierError { } } +impl From for VerifierError { + fn from(e: ark_iop::IorVerifierError) -> Self { + match e { + ark_iop::IorVerifierError::StatementShape { + what, + expected, + got, + } => Self::StatementShape { + what, + expected, + got, + }, + ark_iop::IorVerifierError::Target => Self::Target, + ark_iop::IorVerifierError::Transcript(_) => Self::SpongeFish, + ark_iop::IorVerifierError::Custom(_) => Self::Target, + } + } +} + impl From for VerifierError { fn from(err: effsc::proof::SumcheckError) -> Self { use effsc::proof::SumcheckError; diff --git a/src/protocol/iors/batching.rs b/src/iop/iors/batching.rs similarity index 80% rename from src/protocol/iors/batching.rs rename to src/iop/iors/batching.rs index 90d9a50..21d1a7c 100644 --- a/src/protocol/iors/batching.rs +++ b/src/iop/iors/batching.rs @@ -3,6 +3,9 @@ //! sparse-evaluation optimization. use ark_ff::{Field, PrimeField}; +use ark_iop::{ + IorProveResult, IorProverError, IorVerifierError, IorVerifyResult, ProverTriple, IOR, +}; use ark_std::log2; use effsc::{ noop_hook, provers::inner_product::InnerProductProver, runner::sumcheck, @@ -13,10 +16,7 @@ use std::collections::HashMap; use std::marker::PhantomData; use crate::count_ops; -use crate::error::{ProverError, VerifierError}; -use crate::protocol::ior::{ProverTriple, IOR}; -use crate::protocol::oracles::evaluation::Oracle; -use crate::protocol::transcript::EffscVerifierTranscript; +use crate::iop::oracles::evaluation::Oracle; use crate::utils::poly::{eq_poly, eq_poly_non_binary}; /// Sparse-eval optimization (CBBZ23 / HyperPlonk): shift-query zetas at @@ -125,6 +125,7 @@ where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { const NAME: &'static str = "Batching"; + const MESSAGE_TAGS: &'static [&'static str] = &["squeeze:xis", "delegate:effsc.sumcheck"]; type Statement<'b> = BatchingStatement @@ -160,27 +161,28 @@ where alpha_sumcheck_challenges: inputs.alpha_sumcheck_challenges.clone(), } } +} +impl Batching +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, +{ #[tracing::instrument( name = "batching", skip_all, fields(s = statement.s_num_ood_samples, t = statement.t_num_queries, log_n = statement.log_n) )] - fn prove_inner<'b>( + fn prove_inner( &self, prover_state: &mut ProverState, - statement: &Self::Statement<'b>, - _witness: &Self::Witness<'b>, - inputs: &Self::ProverInputs<'b>, - ) -> ProverTriple - where - Self: 'b, - { + statement: &BatchingStatement, + inputs: &BatchingProverInputs<'_, F>, + ) -> ProverTriple, (), BatchingReducedWitness> { let n = inputs.oracle.len(); let r = 1 + statement.s_num_ood_samples + statement.t_num_queries; let log_r = log2(r) as usize; if statement.zetas_prefix.len() != r { - return Err(ProverError::StatementShape { + return Err(IorProverError::StatementShape { what: "zetas_prefix", expected: r, got: statement.zetas_prefix.len(), @@ -212,7 +214,7 @@ where ) }; - // Run the inner-product sumcheck. MSB half-split → reverse once. + // Inner-product sumcheck. MSB half-split → reverse once. let alpha = { let _s = tracing::info_span!("batching.sumcheck").entered(); let log_n_bits = ark_std::log2(n) as u64; @@ -245,49 +247,41 @@ where skip_all, fields(s = statement.s_num_ood_samples, t = statement.t_num_queries, log_n = statement.log_n) )] - fn verify_inner<'b, 'c>( + fn verify_inner( &self, - verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement<'c>, - inputs: &Self::VerifierInputs<'c>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> - where - Self: 'c, - { + verifier_state: &mut VerifierState<'_>, + statement: &BatchingStatement, + inputs: &BatchingVerifierInputs, + ) -> Result<(BatchingReductionInputs, ()), IorVerifierError> { let r = 1 + statement.s_num_ood_samples + statement.t_num_queries; let log_r = log2(r) as usize; if statement.zetas_prefix.len() != r { - return Err(VerifierError::StatementShape { + return Err(IorVerifierError::StatementShape { what: "zetas_prefix", expected: r, got: statement.zetas_prefix.len(), }); } if inputs.nus_claimed_evals.len() != r { - return Err(VerifierError::StatementShape { + return Err(IorVerifierError::StatementShape { what: "nus_claimed_evals", expected: r, got: inputs.nus_claimed_evals.len(), }); } - // Squeeze ξ matching the prover. let xis: Vec = (0..log_r) .map(|_| verifier_state.verifier_message::()) .collect(); let xi_eq_evals = (0..r).map(|i| eq_poly(&xis, i)).collect::>(); - // σ₂ = Σ ξ_eq · ν. let sigma_2 = xi_eq_evals .iter() .zip(&inputs.nus_claimed_evals) .fold(F::zero(), |acc, (xi_eq, nu)| acc + *xi_eq * nu); - // Run sumcheck_verify and check the final-claim oracle check. - let res = { - let mut wrap = EffscVerifierTranscript(verifier_state); - sumcheck_verify(sigma_2, 2, statement.log_n, &mut wrap, |_, _| Ok(()))? - }; + let res = sumcheck_verify(sigma_2, 2, statement.log_n, verifier_state, |_, _| Ok(())) + .map_err(|e| IorVerifierError::Transcript(format!("sumcheck: {e:?}")))?; let alpha_lsb: Vec = res.challenges.iter().rev().copied().collect(); let mut zeta_eqs = Vec::with_capacity(r); @@ -301,7 +295,7 @@ where .fold(F::zero(), |acc, (a, b)| acc + a * *b); (expected == res.final_claim) .then_some(()) - .ok_or(VerifierError::Target)?; + .ok_or(IorVerifierError::Target)?; Ok(( BatchingReductionInputs { @@ -310,4 +304,30 @@ where (), )) } + + pub fn prove( + &self, + prover_state: &mut ProverState, + statement: &BatchingStatement, + _witness: &(), + inputs: &BatchingProverInputs<'_, F>, + ) -> Result< + IorProveResult, (), BatchingReducedWitness>, + IorProverError, + > { + self.compose_prove(prover_state, statement, |t| { + self.prove_inner(t, statement, inputs) + }) + } + + pub fn verify( + &self, + verifier_state: &mut VerifierState<'_>, + statement: &BatchingStatement, + inputs: &BatchingVerifierInputs, + ) -> Result, ()>, IorVerifierError> { + self.compose_verify(verifier_state, statement, |t| { + self.verify_inner(t, statement, inputs) + }) + } } diff --git a/src/protocol/iors/bridge.rs b/src/iop/iors/bridge.rs similarity index 65% rename from src/protocol/iors/bridge.rs rename to src/iop/iors/bridge.rs index 494a049..b41086e 100644 --- a/src/protocol/iors/bridge.rs +++ b/src/iop/iors/bridge.rs @@ -1,6 +1,10 @@ //! TC → OOD bridge IOR. Publishes `(td_new, η, ν₀)` and discharges //! TwinConstraint's deferred oracle check `eq(τ,γ)·(ν₀ + ω·η) ≟ final_claim`. + use ark_ff::Field; +use ark_iop::{ + IorProveResult, IorProverError, IorVerifierError, IorVerifyResult, ProverTriple, IOR, +}; use ark_vc::mvc::MultiVectorCommitment; use ark_vc::vc::VectorCommitment; use effsc::hypercube::compute_hypercube_eq_evals; @@ -9,10 +13,8 @@ use std::marker::PhantomData; use crate::count_ops; use crate::crypto::vc::CommittedCodewords; -use crate::error::{ProverError, VerifierError}; -use crate::protocol::ior::{ProverTriple, IOR}; -use crate::protocol::iors::twin_constraint::DeferredOracleCheck; -use crate::protocol::oracles::evaluation::Oracle; +use crate::iop::iors::twin_constraint::DeferredOracleCheck; +use crate::iop::oracles::evaluation::Oracle; use crate::relations::PolyPredicate; pub struct BridgeStatement { @@ -89,6 +91,8 @@ where V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize + Clone, { const NAME: &'static str = "Bridge"; + const MESSAGE_TAGS: &'static [&'static str] = + &["send:td_new_commitment", "send:eta", "send:nu_0"]; type Statement<'b> = BridgeStatement @@ -126,44 +130,46 @@ where td_new_commitment: inputs.td_new_commitment.clone(), } } +} +impl Bridge +where + F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + P: PolyPredicate, + V: MultiVectorCommitment, + V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize + Clone, +{ #[tracing::instrument( name = "bridge", skip_all, fields(log_m = statement.log_m, n_minus_k = statement.n_minus_k) )] - fn prove_inner<'a>( + fn prove_inner( &self, prover_state: &mut ProverState, - statement: &Self::Statement<'a>, - witness: &Self::Witness<'a>, - inputs: &Self::ProverInputs<'a>, - ) -> ProverTriple - where - Self: 'a, - { - // η = ⟨ eq(β_τ), p(z) ⟩ + statement: &BridgeStatement, + witness: &BridgeWitness<'_, F>, + inputs: &BridgeProverInputs<'_, F, P, V>, + ) -> ProverTriple, (), BridgeReducedWitness> { let beta_eq_evals = compute_hypercube_eq_evals(statement.log_m, &statement.beta_tau); let eta = inputs .predicate .evaluate_bundled(&beta_eq_evals, witness.z_witness_assignment) - .map_err(|_| ProverError::SpongeFish)?; + .map_err(|e| IorProverError::Custom(format!("predicate eval: {e}")))?; - // ν₀ = f̂(ζ₀) let nu_0 = witness.f_oracle.query_at_point(&statement.zeta_0); - // (new_x, new_w) = z[..N-k], z[N-k..] let (new_x_slice, new_w_slice) = witness.z_witness_assignment.split_at(statement.n_minus_k); let new_x = new_x_slice.to_vec(); let new_w = new_w_slice.to_vec(); - // td_new ← V.commit(f.evals()) let td_new = { let _s = tracing::info_span!("bridge.commit_new_oracle").entered(); count_ops!(MerkleTreeBuilds); let codeword = witness.f_oracle.evals().to_vec(); - let (commitment, state) = ::commit(inputs.ck, codeword.iter()) - .map_err(|_| ProverError::SpongeFish)?; + let (commitment, state) = + ::commit(inputs.ck, codeword.iter()) + .map_err(|e| IorProverError::Custom(format!("commit: {e}")))?; CommittedCodewords:: { commitment, state, @@ -172,7 +178,6 @@ where }; let td_new_commitment: V::Commitment = td_new.commitment.clone(); - // Absorb (td_new.commitment, η, ν₀) prover_state.prover_message(&td_new_commitment); prover_state.prover_message(&eta); prover_state.prover_message(&nu_0); @@ -193,24 +198,26 @@ where } #[tracing::instrument(name = "bridge.verify", skip_all)] - fn verify_inner<'a, 'b>( + fn verify_inner( &self, - verifier_state: &mut VerifierState<'a>, - _statement: &Self::Statement<'b>, - inputs: &Self::VerifierInputs<'b>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> - where - Self: 'b, - { - // Read (commitment, η, ν₀) from the transcript. - let td_new_commitment: V::Commitment = verifier_state.prover_message()?; - let eta: F = verifier_state.prover_message()?; - let nu_0: F = verifier_state.prover_message()?; + verifier_state: &mut VerifierState<'_>, + _statement: &BridgeStatement, + inputs: &BridgeVerifierInputs<'_, F>, + ) -> Result<(BridgeReductionInputs, ()), IorVerifierError> { + let td_new_commitment: V::Commitment = verifier_state + .prover_message() + .map_err(|e| IorVerifierError::Transcript(e.to_string()))?; + let eta: F = verifier_state + .prover_message() + .map_err(|e| IorVerifierError::Transcript(e.to_string()))?; + let nu_0: F = verifier_state + .prover_message() + .map_err(|e| IorVerifierError::Transcript(e.to_string()))?; - // Discharge TC's deferred check. inputs .deferred - .discharge(inputs.gamma_twin_constraint_challenges, nu_0, eta)?; + .discharge(inputs.gamma_twin_constraint_challenges, nu_0, eta) + .map_err(|_| IorVerifierError::Target)?; Ok(( BridgeReductionInputs { @@ -221,4 +228,31 @@ where (), )) } + + #[allow(clippy::type_complexity)] + pub fn prove( + &self, + prover_state: &mut ProverState, + statement: &BridgeStatement, + witness: &BridgeWitness<'_, F>, + inputs: &BridgeProverInputs<'_, F, P, V>, + ) -> Result< + IorProveResult, (), BridgeReducedWitness>, + IorProverError, + > { + self.compose_prove(prover_state, statement, |t| { + self.prove_inner(t, statement, witness, inputs) + }) + } + + pub fn verify( + &self, + verifier_state: &mut VerifierState<'_>, + statement: &BridgeStatement, + inputs: &BridgeVerifierInputs<'_, F>, + ) -> Result, ()>, IorVerifierError> { + self.compose_verify(verifier_state, statement, |t| { + self.verify_inner(t, statement, inputs) + }) + } } diff --git a/src/protocol/iors/mod.rs b/src/iop/iors/mod.rs similarity index 100% rename from src/protocol/iors/mod.rs rename to src/iop/iors/mod.rs diff --git a/src/protocol/iors/ood.rs b/src/iop/iors/ood.rs similarity index 64% rename from src/protocol/iors/ood.rs rename to src/iop/iors/ood.rs index 7b41383..8b265f4 100644 --- a/src/protocol/iors/ood.rs +++ b/src/iop/iors/ood.rs @@ -1,13 +1,14 @@ //! Out-of-domain sampling IOR. use ark_ff::{Field, PrimeField}; +use ark_iop::{ + IorProveResult, IorProverError, IorVerifierError, IorVerifyResult, ProverTriple, IOR, +}; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; use crate::count_ops; -use crate::error::VerifierError; -use crate::protocol::ior::{ProverTriple, IOR}; -use crate::protocol::oracles::evaluation::Oracle; +use crate::iop::oracles::evaluation::Oracle; pub struct OodStatement { pub s_num_ood_samples: usize, @@ -36,6 +37,7 @@ where F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, { const NAME: &'static str = "OOD"; + const MESSAGE_TAGS: &'static [&'static str] = &["squeeze:samples", "send:answers"]; type Statement<'b> = OodStatement @@ -72,18 +74,19 @@ where answers: inputs.answers.clone(), } } +} +impl Ood +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, +{ #[tracing::instrument(name = "ood", skip_all, fields(s = statement.s_num_ood_samples, log_n = statement.log_n))] - fn prove_inner<'b>( + fn prove_inner( &self, prover_state: &mut ProverState, - statement: &Self::Statement<'b>, - _witness: &Self::Witness<'b>, - inputs: &Self::ProverInputs<'b>, - ) -> ProverTriple - where - Self: 'b, - { + statement: &OodStatement, + inputs: &OodProverInputs<'_, F>, + ) -> ProverTriple, (), ()> { let samples_flat = prover_state.verifier_messages_vec::(statement.s_num_ood_samples * statement.log_n); count_ops!(OodPointQueries, statement.s_num_ood_samples as u64); @@ -107,19 +110,17 @@ where skip_all, fields(s = statement.s_num_ood_samples, log_n = statement.log_n) )] - fn verify_inner<'b, 'c>( + fn verify_inner( &self, - verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement<'c>, - _inputs: &Self::VerifierInputs<'c>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> - where - Self: 'c, - { + verifier_state: &mut VerifierState<'_>, + statement: &OodStatement, + ) -> Result<(OodReductionInputs, ()), IorVerifierError> { let samples_flat: Vec = (0..statement.s_num_ood_samples * statement.log_n) .map(|_| verifier_state.verifier_message::()) .collect(); - let answers: Vec = verifier_state.prover_messages_vec(statement.s_num_ood_samples)?; + let answers: Vec = verifier_state + .prover_messages_vec(statement.s_num_ood_samples) + .map_err(|e| IorVerifierError::Transcript(e.to_string()))?; Ok(( OodReductionInputs { samples_flat, @@ -128,4 +129,27 @@ where (), )) } + + pub fn prove( + &self, + prover_state: &mut ProverState, + statement: &OodStatement, + _witness: &(), + inputs: &OodProverInputs<'_, F>, + ) -> Result, (), ()>, IorProverError> { + self.compose_prove(prover_state, statement, |t| { + self.prove_inner(t, statement, inputs) + }) + } + + pub fn verify( + &self, + verifier_state: &mut VerifierState<'_>, + statement: &OodStatement, + _inputs: &(), + ) -> Result, ()>, IorVerifierError> { + self.compose_verify(verifier_state, statement, |t| { + self.verify_inner(t, statement) + }) + } } diff --git a/src/protocol/iors/pesat.rs b/src/iop/iors/pesat.rs similarity index 72% rename from src/protocol/iors/pesat.rs rename to src/iop/iors/pesat.rs index e376b0a..eedc78b 100644 --- a/src/protocol/iors/pesat.rs +++ b/src/iop/iors/pesat.rs @@ -4,17 +4,18 @@ //! joint-commit path (one commitment over all l1 codewords), absorbs //! that commitment + code evaluations, and derives the τ zero-check //! challenges. -//! + use ark_codes::traits::LinearCode; use ark_ff::{Field, PrimeField}; +use ark_iop::{ + IorProveResult, IorProverError, IorVerifierError, IorVerifyResult, ProverTriple, IOR, +}; use ark_vc::mvc::MultiVectorCommitment; use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; use crate::count_ops; use crate::crypto::vc::CommittedCodewords; -use crate::error::VerifierError; -use crate::protocol::ior::{ProverTriple, IOR}; pub struct PesatStatement { pub l1_first_fold_factor: usize, @@ -73,6 +74,8 @@ where V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize, { const NAME: &'static str = "PESAT"; + const MESSAGE_TAGS: &'static [&'static str] = + &["send:rt_0_commitment", "send:mus", "squeeze:taus"]; type Statement<'b> = PesatStatement where @@ -108,23 +111,26 @@ where taus_zero_check_challenges: inputs.taus_zero_check_challenges.clone(), } } +} +impl<'a, F, C, V> Pesat<'a, F, C, V> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + C: LinearCode, + V: MultiVectorCommitment, + V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize, +{ #[tracing::instrument( name = "pesat", skip_all, fields(l1 = statement.l1_first_fold_factor, log_m = statement.log_m, n_witnesses = witness.witnesses.len()) )] - fn prove_inner<'b>( + fn prove_inner( &self, prover_state: &mut ProverState, - statement: &Self::Statement<'b>, - witness: &Self::Witness<'b>, - _inputs: &Self::ProverInputs<'b>, - ) -> ProverTriple - where - 'a: 'b, - V: 'b, - { + statement: &PesatStatement, + witness: &PesatWitness<'_, F>, + ) -> ProverTriple, (), PesatReducedWitness> { let codewords: Vec> = { let _s = tracing::info_span!("pesat.encode").entered(); count_ops!(EncodeCalls, witness.witnesses.len() as u64); @@ -178,18 +184,17 @@ where skip_all, fields(l1 = statement.l1_first_fold_factor, log_m = statement.log_m) )] - fn verify_inner<'b, 'c>( + fn verify_inner( &self, - verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement<'c>, - _inputs: &Self::VerifierInputs<'c>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> - where - 'a: 'c, - V: 'c, - { - let rt_0: V::Commitment = verifier_state.prover_message()?; - let mus: Vec = verifier_state.prover_messages_vec(statement.l1_first_fold_factor)?; + verifier_state: &mut VerifierState<'_>, + statement: &PesatStatement, + ) -> Result<(PesatReductionInputs, PesatVerifierOutputs), IorVerifierError> { + let rt_0: V::Commitment = verifier_state + .prover_message() + .map_err(|e| IorVerifierError::Transcript(e.to_string()))?; + let mus: Vec = verifier_state + .prover_messages_vec(statement.l1_first_fold_factor) + .map_err(|e| IorVerifierError::Transcript(e.to_string()))?; let taus: Vec> = (0..statement.l1_first_fold_factor) .map(|_| { (0..statement.log_m) @@ -208,4 +213,34 @@ where }, )) } + + #[allow(clippy::type_complexity)] + pub fn prove( + &self, + prover_state: &mut ProverState, + statement: &PesatStatement, + witness: &PesatWitness<'_, F>, + _inputs: &(), + ) -> Result< + IorProveResult, (), PesatReducedWitness>, + IorProverError, + > { + self.compose_prove(prover_state, statement, |t| { + self.prove_inner(t, statement, witness) + }) + } + + pub fn verify( + &self, + verifier_state: &mut VerifierState<'_>, + statement: &PesatStatement, + _inputs: &(), + ) -> Result< + IorVerifyResult, PesatVerifierOutputs>, + IorVerifierError, + > { + self.compose_verify(verifier_state, statement, |t| { + self.verify_inner(t, statement) + }) + } } diff --git a/src/protocol/iors/proximity.rs b/src/iop/iors/proximity.rs similarity index 72% rename from src/protocol/iors/proximity.rs rename to src/iop/iors/proximity.rs index 3e82d0f..aa573f7 100644 --- a/src/protocol/iors/proximity.rs +++ b/src/iop/iors/proximity.rs @@ -2,28 +2,27 @@ //! //! Index queries on the committed oracles. Opens both the fresh PESAT //! commitment and each accumulated commitment at the query positions. -//! With the trait migration, opening proofs (auth paths + sibling -//! digests) are written into the spongefish transcript by -//! `V::open_multiple` rather than carried as separate proof fields. -//! +//! Opening proofs (auth paths + sibling digests) are written into the +//! spongefish transcript by `V::open_multiple` rather than carried as +//! separate proof fields. + use ark_ff::Field; +use ark_iop::{ + IndexedOracle, IorProveResult, IorProverError, IorVerifierError, IorVerifyResult, ProverTriple, + IOR, +}; use ark_vc::mvc::MultiVectorCommitment; use spongefish::{Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; use crate::count_ops; use crate::crypto::vc::CommittedCodewords; -use crate::error::VerifierError; -use crate::protocol::ior::{ProverTriple, IOR}; -use crate::protocol::oracles::indexed::IndexedOracle; -use crate::protocol::oracles::query_indices::QueryIndices; +use crate::iop::oracles::query_indices::QueryIndices; pub struct ProximityStatement { pub queries: QueryIndices, pub l2_second_fold_factor: usize, pub t_num_queries: usize, - /// Codeword length (`code.code_len()`), needed by both prover and - /// verifier. pub n_code_len: usize, } @@ -50,15 +49,14 @@ where } /// Wire-format proof string. Auth paths + sibling digests now live in -/// the spongefish transcript (written by `V::open_multiple`); only the -/// shift-query answers remain as out-of-band data. +/// the spongefish transcript; only the shift-query answers remain +/// out-of-band. pub struct ProximityProofString { /// Per-query × per-codeword. Outer length = t (queries). Inner /// length = (l2 acc + l1 fresh). pub shift_query_answers: Vec>, } -/// Proximity IOR configuration. Holds the trait CK used by `open_multiple`. pub struct Proximity<'a, F, V> where F: Field, @@ -75,6 +73,7 @@ where V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize, { const NAME: &'static str = "Proximity"; + const MESSAGE_TAGS: &'static [&'static str] = &["delegate:vc.open_multiple"]; type Statement<'b> = ProximityStatement @@ -89,7 +88,7 @@ where where Self: 'b; type VerifierInputs<'b> - = ProximityVerifierInputs<'b, F, crate::protocol::oracles::indexed::ValidatedOracle> + = ProximityVerifierInputs<'b, F, ark_iop::ValidatedOracle> where Self: 'b; type ReductionInputs = (); @@ -107,7 +106,14 @@ where Self: 'b, { } +} +impl<'a, F, V> Proximity<'a, F, V> +where + F: Field, + V: MultiVectorCommitment, + V::Commitment: Encoding<[u8]> + NargSerialize + NargDeserialize, +{ #[tracing::instrument( name = "proximity", skip_all, @@ -116,27 +122,18 @@ where n_accumulators = inputs.acc_td_committed_codewords.len(), ) )] - fn prove_inner<'b>( + fn prove_inner( &self, prover_state: &mut ProverState, - statement: &Self::Statement<'b>, - _witness: &Self::Witness<'b>, - inputs: &Self::ProverInputs<'b>, - ) -> ProverTriple - where - Self: 'b, - 'a: 'b, - V: 'b, - { + statement: &ProximityStatement, + inputs: &ProximityProverInputs<'_, F, V>, + ) -> ProverTriple<(), ProximityProofString, ()> { let leaf_positions = &statement.queries.leaf_positions; - // Trait `open_multiple` requires sorted unique indices. let mut sorted_unique = leaf_positions.clone(); sorted_unique.sort_unstable(); sorted_unique.dedup(); - // Helper: column-tuple values at the sorted positions for one - // commitment's codewords. let column_tuples = |codewords: &[Vec]| -> Vec> { sorted_unique .iter() @@ -185,8 +182,6 @@ where } } - // Shift query answers: per query position, values across - // (acc_codewords ++ fresh_codewords). let shift_query_answers = { let _s = tracing::info_span!("proximity.shift_queries").entered(); let total_codewords = inputs @@ -226,39 +221,30 @@ where skip_all, fields(t = statement.t_num_queries, l2 = statement.l2_second_fold_factor) )] - fn verify_inner<'b, 'c>( + fn verify_inner( &self, - _verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement<'c>, - inputs: &Self::VerifierInputs<'c>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> - where - Self: 'c, - 'a: 'c, - V: 'c, - { - // Arity check. + _verifier_state: &mut VerifierState<'_>, + statement: &ProximityStatement, + inputs: &ProximityVerifierInputs<'_, F, ark_iop::ValidatedOracle>, + ) -> Result<((), ()), IorVerifierError> { (inputs.acc.len() == statement.l2_second_fold_factor) .then_some(()) - .ok_or(VerifierError::NumL2Instances)?; + .ok_or_else(|| { + IorVerifierError::Custom(format!( + "Proximity: NumL2Instances mismatch (got {}, expected {})", + inputs.acc.len(), + statement.l2_second_fold_factor + )) + })?; - // Validation already happened upstream (orchestrator called - // V::check_multiple before invoking this IOR). Handles are - // pre-validated; this IOR is BCS-agnostic. - inputs - .fresh - .validate() - .then_some(()) - .ok_or(VerifierError::ShiftQuery)?; + // Validation by construction: ValidatedOracle's existence + // already attests the orchestrator ran V::check_multiple + // upstream. No runtime validate() call needed. count_ops!( MerklePathsVerified, statement.queries.leaf_positions.len() as u64 ); - for handle in inputs.acc.iter() { - handle - .validate() - .then_some(()) - .ok_or(VerifierError::ShiftQuery)?; + for _ in inputs.acc.iter() { count_ops!( MerklePathsVerified, statement.queries.leaf_positions.len() as u64 @@ -267,4 +253,27 @@ where Ok(((), ())) } + + pub fn prove( + &self, + prover_state: &mut ProverState, + statement: &ProximityStatement, + _witness: &(), + inputs: &ProximityProverInputs<'_, F, V>, + ) -> Result, ()>, IorProverError> { + self.compose_prove(prover_state, statement, |t| { + self.prove_inner(t, statement, inputs) + }) + } + + pub fn verify( + &self, + verifier_state: &mut VerifierState<'_>, + statement: &ProximityStatement, + inputs: &ProximityVerifierInputs<'_, F, ark_iop::ValidatedOracle>, + ) -> Result, IorVerifierError> { + self.compose_verify(verifier_state, statement, |t| { + self.verify_inner(t, statement, inputs) + }) + } } diff --git a/src/protocol/iors/sample_queries.rs b/src/iop/iors/sample_queries.rs similarity index 59% rename from src/protocol/iors/sample_queries.rs rename to src/iop/iors/sample_queries.rs index 5286c92..b371454 100644 --- a/src/protocol/iors/sample_queries.rs +++ b/src/iop/iors/sample_queries.rs @@ -3,18 +3,16 @@ //! Between OOD and Batching, the verifier draws `t · log_n` random //! bits and decodes them into `t` shift-query positions over //! `{0, 1}^log_n`, plus their corresponding evaluation-point vectors. -//! -//! This is the simplest possible IOR — one round of pure verifier -//! randomness, no prover messages, no oracles. Modeled as an IOR for -//! uniformity with the rest of the choreography. -//! + use ark_ff::Field; -use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; +use ark_iop::{ + IorProveResult, IorProverError, IorVerifierError, IorVerifyResult, ProverTriple, + VerifierTranscript, IOR, +}; +use spongefish::{ProverState, VerifierState}; use std::marker::PhantomData; -use crate::error::VerifierError; -use crate::protocol::ior::{ProverTriple, IOR}; -use crate::protocol::oracles::query_indices::QueryIndices; +use crate::iop::oracles::query_indices::QueryIndices; pub struct SampleQueriesStatement { pub log_n: usize, @@ -32,11 +30,9 @@ pub struct SampleQueriesReducedStatement { #[derive(Default)] pub struct SampleQueries(PhantomData); -impl IOR for SampleQueries -where - F: Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, -{ +impl IOR for SampleQueries { const NAME: &'static str = "SampleQueries"; + const MESSAGE_TAGS: &'static [&'static str] = &["squeeze:queries"]; type Statement<'b> = SampleQueriesStatement @@ -72,39 +68,51 @@ where queries: inputs.queries.clone(), } } +} +impl SampleQueries { #[tracing::instrument(name = "sample_queries", skip_all, fields(t = statement.t_num_queries, log_n = statement.log_n))] - fn prove_inner<'b>( + fn prove_inner( &self, prover_state: &mut ProverState, - statement: &Self::Statement<'b>, - _witness: &Self::Witness<'b>, - _inputs: &Self::ProverInputs<'b>, - ) -> ProverTriple - where - Self: 'b, - { + statement: &SampleQueriesStatement, + ) -> ProverTriple, (), ()> { let queries = QueryIndices::::sample(prover_state, statement.log_n, statement.t_num_queries); Ok((SampleQueriesReductionInputs { queries }, (), ())) } #[tracing::instrument(name = "sample_queries.verify", skip_all)] - fn verify_inner<'b, 'c>( + fn verify_inner( &self, - verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement<'c>, - _inputs: &Self::VerifierInputs<'c>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> - where - Self: 'c, - { + verifier_state: &mut VerifierState<'_>, + statement: &SampleQueriesStatement, + ) -> Result<(SampleQueriesReductionInputs, ()), IorVerifierError> { let n_bytes = (statement.t_num_queries * statement.log_n).div_ceil(8); - let bytes: Vec = (0..n_bytes) - .map(|_| verifier_state.verifier_message::<[u8; 1]>()[0]) - .collect(); + let bytes = verifier_state.squeeze_bytes(n_bytes); let queries = QueryIndices::from_squeezed_bytes(&bytes, statement.log_n, statement.t_num_queries); Ok((SampleQueriesReductionInputs { queries }, ())) } + + pub fn prove( + &self, + prover_state: &mut ProverState, + statement: &SampleQueriesStatement, + _witness: &(), + _inputs: &(), + ) -> Result, (), ()>, IorProverError> { + self.compose_prove(prover_state, statement, |t| self.prove_inner(t, statement)) + } + + pub fn verify( + &self, + verifier_state: &mut VerifierState<'_>, + statement: &SampleQueriesStatement, + _inputs: &(), + ) -> Result, ()>, IorVerifierError> { + self.compose_verify(verifier_state, statement, |t| { + self.verify_inner(t, statement) + }) + } } diff --git a/src/protocol/iors/twin_constraint.rs b/src/iop/iors/twin_constraint.rs similarity index 73% rename from src/protocol/iors/twin_constraint.rs rename to src/iop/iors/twin_constraint.rs index 21f9406..6b8f873 100644 --- a/src/protocol/iors/twin_constraint.rs +++ b/src/iop/iors/twin_constraint.rs @@ -3,6 +3,9 @@ //! Paired spec: `docs/paper-mods/mod1_oracle.tex`. use ark_ff::{Field, PrimeField}; +use ark_iop::{ + IorProveResult, IorProverError, IorVerifierError, IorVerifyResult, ProverTriple, IOR, +}; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; use ark_vc::mvc::MultiVectorCommitment; use effsc::{ @@ -17,37 +20,29 @@ use effsc::{ use spongefish::{Decoding, Encoding, NargDeserialize, NargSerialize, ProverState, VerifierState}; use std::marker::PhantomData; +use crate::accumulation_scheme::AccumulatorInstance; use crate::count_ops; -use crate::error::{ProverError, VerifierError}; -use crate::protocol::ior::{ProverTriple, IOR}; -use crate::protocol::oracles::evaluation::Oracle; -use crate::protocol::transcript::EffscVerifierTranscript; +use crate::error::VerifierError; +use crate::iop::oracles::evaluation::Oracle; use crate::relations::r1cs::R1CSConstraints; use crate::utils::{ concat_slices, poly::{eq_poly, eq_poly_non_binary}, scale_and_sum, }; -use crate::warp::AccumulatorInstance; /// Degree-1 polynomial interpolating two field elements: `lo + (hi - lo)·X`. fn linear_poly(lo: F, hi: F) -> DensePolynomial { DensePolynomial::from_coefficients_vec(vec![lo, hi - lo]) } -/// A single R1CS constraint row: sparse representations of A, B, and C. type R1CSConstraint = (Vec<(F, usize)>, Vec<(F, usize)>, Vec<(F, usize)>); -/// Evaluate one R1CS constraint `Az·Bz - Cz` as a degree-2 polynomial -/// from two witness vectors `z0`, `z1`. fn eval_r1cs_constraint_poly( (a, b, c): &R1CSConstraint, z0: &[F], z1: &[F], ) -> DensePolynomial { - // effsc's `final_value` calls `accumulate_pair` once with the odd half - // empty (singleton case after all rounds folded). Treat an empty `z` as - // the all-zero vector so the eval returns `F::ZERO` rather than panicking. let eval = |lc: &[(F, usize)], z: &[F]| { if z.is_empty() { F::ZERO @@ -60,8 +55,6 @@ fn eval_r1cs_constraint_poly( DensePolynomial::from_coefficients_vec(vec![a0 * b0 - c0, a0 * b1 + a1 * b0 - c1, a1 * b1]) } -/// Round-polynomial evaluator fusing α-fold, β-fold, and τ-linear into a -/// single sumcheck pass. struct TwinConstraintEvaluator<'a, F: Field> { r1cs: &'a R1CSConstraints, omega: F, @@ -74,20 +67,12 @@ impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { } fn accumulate_pair(&self, coeffs: &mut [F], tw: &[(&[F], &[F])], pw: &[(F, F)]) { - // tw[0] = (u_even, u_odd), tw[1] = (z_even, z_odd), - // tw[2] = (a_even, a_odd), tw[3] = (b_even, b_odd) - // pw[0] = (tau_even, tau_odd) let (u_even, u_odd) = tw[0]; let (z_even, z_odd) = tw[1]; let (a_even, a_odd) = tw[2]; let (b_even, b_odd) = tw[3]; let (tau_even, tau_odd) = pw[0]; - // Singleton case: effsc's `coefficient_lsb::final_value` calls - // `accumulate_pair` once after all rounds with `tw[i] = (singleton, &[])` - // and `pw[0] = (singleton, F::ZERO)`. Evaluate the polynomial directly - // at the singleton point; emit `[h, -h]` so `g(0) + g(1) == h`, matching - // the convention used by the simple pairwise-only evaluators. if u_odd.is_empty() { let f_val = u_even .iter() @@ -113,7 +98,6 @@ impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { return; } - // f(X) = fold(α, oracle_evals): protogalaxy fold over α pairs and linear polys from u let f = protogalaxy::fold( a_even.iter().zip(a_odd).map(|(&l, &r)| (l, r - l)), u_even @@ -123,7 +107,6 @@ impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { .collect(), ); - // p(X) = fold(β, Az·Bz - Cz): protogalaxy fold over β pairs and R1CS constraint polys let p = protogalaxy::fold( b_even.iter().zip(b_odd).map(|(&l, &r)| (l, r - l)), self.r1cs @@ -132,12 +115,6 @@ impl<'a, F: Field> RoundPolyEvaluator for TwinConstraintEvaluator<'a, F> { .collect(), ); - // h(X) = (f(X) + ω·p(X)) · t(X) where t is linear t_0 + t_1·X. - // Closed form per coefficient: h_i = q_{i-1}·t_1 + q_i·t_0 with - // q_i = f_i + ω·p_i. We accumulate directly into `coeffs` so the - // (f + ω·p) sum and the (·t) multiplication never allocate temporary - // DensePolynomials — the per-pair allocation count drops by 3 - // (the +, the *omega, and the naive_mul each used to allocate). let t0 = tau_even; let t1 = tau_odd - tau_even; let f_coeffs = &f.coeffs; @@ -179,12 +156,6 @@ pub struct TwinConstraintProverInputs<'a, F: Field> { /// Deferred oracle check: `final_claim ≟ eq(τ,γ)·(ν₀ + ω·η)`. Cannot fire /// inside `verify` because ν₀, η arrive on the transcript only after Bridge. -/// -/// **Invariant:** every instance must be discharged before the verifier -/// returns. Fields are private (only constructable inside `TwinConstraint`) -/// and `#[must_use]` makes drop-without-use a compile warning. The -/// orchestrator can additionally assert `is_discharged()` after `verify` to -/// catch any composition that forgets the call. #[must_use = "DeferredOracleCheck must be discharged by the downstream IOR; \ dropping it without calling discharge() leaves the verifier unsound"] pub struct DeferredOracleCheck { @@ -212,10 +183,6 @@ impl DeferredOracleCheck { ok.then_some(()).ok_or(VerifierError::Target) } - /// Returns true once `discharge` has been invoked at least once on this - /// handle. The orchestrator should assert this is true after `verify` - /// returns; an undischarged handle means the protocol composition has a - /// soundness gap. pub fn is_discharged(&self) -> bool { self.discharged.get() } @@ -255,6 +222,8 @@ where V: MultiVectorCommitment, { const NAME: &'static str = "TwinConstraint"; + const MESSAGE_TAGS: &'static [&'static str] = + &["squeeze:omega", "squeeze:tau", "delegate:effsc.sumcheck"]; type Statement<'b> = TwinConstraintStatement @@ -278,9 +247,6 @@ where type ReducedWitness = TwinConstraintReducedWitness; type VerifierOutputs = (); - /// Single source of truth for ζ₀ / β_τ. Both prover and verifier - /// land here with `(ω, τ, γ, final_claim)`; the new accumulator - /// state is computed identically on both sides. fn reduce_statement<'b>( &self, statement: &Self::Statement<'b>, @@ -301,8 +267,6 @@ where ); let zeta_0 = scale_and_sum(&alpha_vecs, &gamma_eq_evals); - // β τ-vectors: accumulated taus first (length l2), then PESAT taus - // (length l1). The τ component of the new β = Σ γ_eq(i) · β_i. let beta_taus: Vec> = statement .acc_instance .beta_twin_pairs @@ -323,34 +287,33 @@ where ), } } +} +impl<'a, F, V> TwinConstraint<'a, F, V> +where + F: Field + PrimeField + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize + NargSerialize, + V: MultiVectorCommitment, +{ #[tracing::instrument( name = "twin_constraint", skip_all, fields(log_l = statement.log_l, log_m = statement.log_m, log_n = statement.log_n) )] - fn prove_inner<'b>( + fn prove_inner( &self, prover_state: &mut ProverState, - statement: &Self::Statement<'b>, - witness: &Self::Witness<'b>, - inputs: &Self::ProverInputs<'b>, - ) -> ProverTriple - where - Self: 'b, - 'a: 'b, - V: 'b, - { + statement: &TwinConstraintStatement, + witness: &TwinConstraintWitness<'_, F>, + inputs: &TwinConstraintProverInputs<'_, F>, + ) -> ProverTriple, (), TwinConstraintReducedWitness> { let l1 = inputs.fresh_codewords.len(); let log_l = statement.log_l; let log_m = statement.log_m; let log_n = statement.log_n; - // a. zero-check randomness let omega: F = prover_state.verifier_message(); let tau = prover_state.verifier_messages_vec::(log_l); - // b. assemble sumcheck tables let tau_eq_evals = Ascending::new(log_l) .map(|p| eq_poly(&tau, p.index)) .collect::>(); @@ -370,7 +333,6 @@ where .map(|(x, w)| concat_slices(x, w)) .collect(); - // β tables: accumulated β-τs first, then PESAT τs. let beta_vecs: Vec> = statement .acc_instance .beta_twin_pairs @@ -380,12 +342,12 @@ where .collect(); let tablewise = vec![ - concat_slices(inputs.acc_codewords, inputs.fresh_codewords), // u - z_vecs, // z - alpha_vecs, // a - beta_vecs, // b + concat_slices(inputs.acc_codewords, inputs.fresh_codewords), + z_vecs, + alpha_vecs, + beta_vecs, ]; - let pw = vec![tau_eq_evals]; // tau + let pw = vec![tau_eq_evals]; let degree = 1 + (log_n + 1).max(log_m + 2); let evaluator = TwinConstraintEvaluator { @@ -394,7 +356,6 @@ where degree, }; - // c. run the sumcheck. let mut cc = CoefficientProverLSB::new(&evaluator, tablewise, pw); let proof = { let _s = tracing::info_span!("twin_constraint.sumcheck").entered(); @@ -402,20 +363,16 @@ where sumcheck(&mut cc, log_l, prover_state, noop_hook) }; if proof.challenges.len() != log_l { - return Err(ProverError::StatementShape { + return Err(IorProverError::StatementShape { what: "sumcheck.challenges", expected: log_l, got: proof.challenges.len(), }); } - // d. pull only the reduced *witness* halves out of CC. ζ₀ and β_τ - // are NOT pulled here — `reduce_statement` recomputes them from - // (statement, γ) via `scale_and_sum`, which is the single source - // of truth that both prover and verifier go through. let reduced = cc.tablewise(); if !reduced.iter().all(|t| t.len() == 1) { - return Err(ProverError::StatementShape { + return Err(IorProverError::StatementShape { what: "sumcheck.tablewise (singleton after full fold)", expected: 1, got: reduced.iter().map(|t| t.len()).max().unwrap_or(0), @@ -444,28 +401,20 @@ where skip_all, fields(log_l = statement.log_l, log_m = statement.log_m, log_n = statement.log_n) )] - fn verify_inner<'b, 'c>( + fn verify_inner( &self, - verifier_state: &mut VerifierState<'b>, - statement: &Self::Statement<'c>, - _inputs: &Self::VerifierInputs<'c>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> - where - Self: 'c, - 'a: 'c, - V: 'c, - { + verifier_state: &mut VerifierState<'_>, + statement: &TwinConstraintStatement, + ) -> Result<(TwinConstraintReductionInputs, ()), IorVerifierError> { let log_l = statement.log_l; let log_n = statement.log_n; let l1 = statement.l1_mus_codeword_first_coords.len(); - // Squeeze ω, τ matching the prover. let omega: F = verifier_state.verifier_message(); let tau: Vec = (0..log_l) .map(|_| verifier_state.verifier_message::()) .collect(); - // Compute σ₁ = Σ_i τ_eq(i) · (μ_i + ω · η_i). let tau_eq_evals = compute_hypercube_eq_evals(log_l, &tau); let etas_l2_first = concat_slices( &statement.acc_instance.eta_predicate_evals, @@ -486,14 +435,10 @@ where acc + eq_tau * (mu + omega * eta) }); - // Run the sumcheck. The deferred oracle check - // final_claim == eq(τ, γ) · (ν₀ + ω · η) - // is left to the orchestrator because ν₀ and η arrive on the - // transcript AFTER the sumcheck rounds. let tc_degree = 1 + (log_n + 1).max(statement.log_m + 2); let (gamma, final_claim) = { - let mut wrap = EffscVerifierTranscript(verifier_state); - let res = sumcheck_verify(sigma_1, tc_degree, log_l, &mut wrap, |_, _| Ok(()))?; + let res = sumcheck_verify(sigma_1, tc_degree, log_l, verifier_state, |_, _| Ok(())) + .map_err(|e| IorVerifierError::Transcript(format!("sumcheck: {e:?}")))?; (res.challenges, res.final_claim) }; @@ -507,4 +452,30 @@ where (), )) } + + pub fn prove( + &self, + prover_state: &mut ProverState, + statement: &TwinConstraintStatement, + witness: &TwinConstraintWitness<'_, F>, + inputs: &TwinConstraintProverInputs<'_, F>, + ) -> Result< + IorProveResult, (), TwinConstraintReducedWitness>, + IorProverError, + > { + self.compose_prove(prover_state, statement, |t| { + self.prove_inner(t, statement, witness, inputs) + }) + } + + pub fn verify( + &self, + verifier_state: &mut VerifierState<'_>, + statement: &TwinConstraintStatement, + _inputs: &(), + ) -> Result, ()>, IorVerifierError> { + self.compose_verify(verifier_state, statement, |t| { + self.verify_inner(t, statement) + }) + } } diff --git a/src/iop/mod.rs b/src/iop/mod.rs new file mode 100644 index 0000000..e0e7e93 --- /dev/null +++ b/src/iop/mod.rs @@ -0,0 +1,2 @@ +pub mod iors; +pub mod oracles; diff --git a/src/protocol/oracles/evaluation.rs b/src/iop/oracles/evaluation.rs similarity index 100% rename from src/protocol/oracles/evaluation.rs rename to src/iop/oracles/evaluation.rs diff --git a/src/iop/oracles/indexed.rs b/src/iop/oracles/indexed.rs new file mode 100644 index 0000000..edb4a86 --- /dev/null +++ b/src/iop/oracles/indexed.rs @@ -0,0 +1,5 @@ +//! Re-exports of the canonical IndexedOracle types from `ark_iop`. +//! Existed locally during the extraction; canonical definitions now +//! live in the ark-iop crate. + +pub use ark_iop::oracle::indexed::{IndexedOracle, ValidatedOracle}; diff --git a/src/protocol/oracles/mod.rs b/src/iop/oracles/mod.rs similarity index 100% rename from src/protocol/oracles/mod.rs rename to src/iop/oracles/mod.rs diff --git a/src/protocol/oracles/query_indices.rs b/src/iop/oracles/query_indices.rs similarity index 95% rename from src/protocol/oracles/query_indices.rs rename to src/iop/oracles/query_indices.rs index b4ca8f6..8441823 100644 --- a/src/protocol/oracles/query_indices.rs +++ b/src/iop/oracles/query_indices.rs @@ -1,5 +1,5 @@ use ark_ff::Field; -use spongefish::ProverState; +use ark_iop::ProverTranscript; #[derive(Clone)] pub struct QueryIndices { @@ -8,17 +8,13 @@ pub struct QueryIndices { } impl QueryIndices { - pub fn sample( - prover_state: &mut ProverState, + pub fn sample( + transcript: &mut P, log_codeword_len: usize, num_queries: usize, ) -> Self { let num_bytes = (num_queries * log_codeword_len).div_ceil(8); - let squeezed_bytes: Vec = prover_state - .verifier_messages_vec::<[u8; 1]>(num_bytes) - .into_iter() - .map(|[b]| b) - .collect(); + let squeezed_bytes = transcript.squeeze_bytes(num_bytes); Self::from_squeezed_bytes(&squeezed_bytes, log_codeword_len, num_queries) } diff --git a/src/lib.rs b/src/lib.rs index 36ced41..d8fb9be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,65 @@ +//! # WarpAccumulationScheme — accumulation scheme with code-based commitments +//! +//! WarpAccumulationScheme folds many R1CS instances into a single, sub-linear-to-check accumulator. +//! Per accumulation round it runs six IORs (Interactive Oracle Reductions) in +//! order through a Fiat-Shamir transcript, producing a new accumulator and a +//! per-round proof. A separate [`WarpAccumulationScheme::decide`] step closes the accumulator. +//! +//! Entry point: [`WarpAccumulationScheme`]. The same type implements [`ark_iop::IOP`] +//! (per-round protocol identity) and [`crate::accumulation_scheme::AccumulationScheme`] +//! (split-accumulation wrapper). One value, two traits. +//! +//! ## End-to-end flow +//! +//! ```text +//! WarpAccumulationScheme::prove ──▶ ((AccumulatorInstance, AccumulatorWitness), WarpProof) +//! WarpAccumulationScheme::verify ──▶ accept the new accumulator instance (or reject) +//! WarpAccumulationScheme::decide ──▶ close the accumulator (final accept / reject) +//! ``` +//! +//! See `examples/hash_chain.rs` for a runnable end-to-end example. +//! +//! ## Where to look +//! +//! - [`WarpAccumulationScheme`] (`src/accumulation_scheme/scheme.rs`) — the scheme value carrying code, ck, predicate. +//! - [`accumulation_scheme::iop`](`crate::accumulation_scheme::iop`) (`src/accumulation_scheme/iop.rs`) — +//! `IOP` + `AccumulationScheme` impls on `WarpAccumulationScheme`. Names the ordered IOR list and provides +//! the FS prologue + decider. +//! - [`accumulation_scheme::trait_def`](`crate::accumulation_scheme::trait_def`) +//! (`src/accumulation_scheme/trait_def.rs`) — the `AccumulationScheme` trait itself, +//! warp-local for now (slated for upstream to `ark-iop` when a second accumulating +//! consumer arrives). +//! - [`iop::iors`](`crate::iop::iors`) (`src/iop/iors/`) — the six IORs run per round, +//! in this order: `Pesat` → `TwinConstraint` → `Bridge` → `Ood` → `SampleQueries` +//! → `Batching`. Plus `Proximity` (FS-transparent). +//! +//! ## Fiat-Shamir hygiene (structural) +//! +//! Two parity invariants are compiled in via [`ark_iop`]: +//! +//! 1. **Reduction parity** — `IOR::compose_prove` / `compose_verify` are +//! the only legal constructors of result types; both funnel through +//! `reduce_statement`. +//! 2. **Message-order parity** — every IOR declares `const MESSAGE_TAGS`; +//! `compose_*` absorbs them as an FS prologue. The outer scheme adds +//! `AccumulationScheme::absorb_scheme_prologue_*` for the protocol-map +//! domain separator. + +pub mod accumulation_scheme; pub mod config; pub mod constraints; pub mod crypto; pub mod error; +pub mod iop; pub mod params; pub mod profile; -pub mod protocol; pub mod relations; pub mod serialize; pub mod utils; -pub mod warp; -pub use crate::config::WARPConfig; -pub use crate::error::{DeciderError, ProverError, VerifierError, WARPError}; -pub use crate::warp::{ - AccumulatorInstance, AccumulatorWitness, WARPProof, WARPProverKey, WARPVerifierKey, WARP, +pub use crate::accumulation_scheme::{ + AccumulatorInstance, AccumulatorWitness, WarpAccumulationScheme, WarpProof, WarpProverKey, + WarpVerifierKey, }; +pub use crate::config::WarpConfig; +pub use crate::error::{DeciderError, ProverError, VerifierError, WarpError}; diff --git a/src/params/mod.rs b/src/params/mod.rs index 8fc1ded..f51f0d2 100644 --- a/src/params/mod.rs +++ b/src/params/mod.rs @@ -1,4 +1,4 @@ -//! Soundness-driven `(s, t)` selection for WARP. +//! Soundness-driven `(s, t)` selection for WarpAccumulationScheme. //! Paired spec: `docs/paper-mods/mod4_parameter_selection.tex`. pub mod presets; diff --git a/src/protocol/ior.rs b/src/protocol/ior.rs deleted file mode 100644 index 4ef3683..0000000 --- a/src/protocol/ior.rs +++ /dev/null @@ -1,176 +0,0 @@ -//! The Interactive Oracle Reduction abstraction. Paired spec: -//! `docs/paper-mods/mod1_oracle.tex`. - -use spongefish::{ProverState, VerifierState}; - -use crate::error::{ProverError, VerifierError}; - -/// Return type of [`IOR::prove_inner`] / [`IOR::prove`]: -/// `Result<(stmt-like, proof-string, reduced-witness), _>`. -pub type ProverTriple = Result<(A, P, W), ProverError>; - -/// Interactive Oracle Reduction. `(stmt, wit, oracles_in) -> (stmt', -/// oracles_out)`. Implementors write `prove_inner` / `verify_inner` / -/// `reduce_statement`; the default `prove` / `verify` chain them so the -/// reduced statement is computed in one place and prover/verifier -/// cannot drift. -pub trait IOR { - const NAME: &'static str; - - type Statement<'a> - where - Self: 'a; - type Witness<'a> - where - Self: 'a; - type ProverInputs<'a> - where - Self: 'a; - type VerifierInputs<'a> - where - Self: 'a; - type ReductionInputs; - type ReducedStatement; - type ProofString; - type ReducedWitness; - type VerifierOutputs; - - /// Single source of truth: both sides feed their `ReductionInputs` here. - fn reduce_statement<'a>( - &self, - statement: &Self::Statement<'a>, - inputs: &Self::ReductionInputs, - ) -> Self::ReducedStatement - where - Self: 'a; - - fn prove_inner<'a>( - &self, - prover_state: &mut ProverState, - statement: &Self::Statement<'a>, - witness: &Self::Witness<'a>, - inputs: &Self::ProverInputs<'a>, - ) -> ProverTriple - where - Self: 'a; - - fn verify_inner<'a, 'b>( - &self, - verifier_state: &mut VerifierState<'a>, - statement: &Self::Statement<'b>, - inputs: &Self::VerifierInputs<'b>, - ) -> Result<(Self::ReductionInputs, Self::VerifierOutputs), VerifierError> - where - Self: 'b; - - fn prove<'a>( - &self, - prover_state: &mut ProverState, - statement: &Self::Statement<'a>, - witness: &Self::Witness<'a>, - inputs: &Self::ProverInputs<'a>, - ) -> ProverTriple - where - Self: 'a, - { - let (red_inputs, proof, red_wit) = - self.prove_inner(prover_state, statement, witness, inputs)?; - let reduced = self.reduce_statement(statement, &red_inputs); - Ok((reduced, proof, red_wit)) - } - - fn verify<'a, 'b>( - &self, - verifier_state: &mut VerifierState<'a>, - statement: &Self::Statement<'b>, - inputs: &Self::VerifierInputs<'b>, - ) -> Result<(Self::ReducedStatement, Self::VerifierOutputs), VerifierError> - where - Self: 'b, - { - let (red_inputs, vouts) = self.verify_inner(verifier_state, statement, inputs)?; - let reduced = self.reduce_statement(statement, &red_inputs); - Ok((reduced, vouts)) - } -} - -/// Single-shot protocol-map domain separator: absorbs the ordered sequence -/// of FS-affecting IOR names into the transcript before any messages or -/// challenges are exchanged. Pairs identically on prover and verifier. -/// -/// Per-IOR labels would be the more obvious design, but WARP intentionally -/// orders FS-transparent IORs (Proximity) differently on the prover vs the -/// verifier; absorbing labels mid-stream would diverge the two transcripts. -/// Committing the full sequence up-front catches permutations of the -/// FS-affecting IORs without disturbing the FS-transparent ones. -pub fn absorb_protocol_map_prover(prover_state: &mut ProverState, names: &[&'static str]) { - prover_state.public_message(b"WARP.protocol_map"); - for name in names { - prover_state.public_message(name.as_bytes()); - prover_state.public_message(b"|"); - } -} - -/// Verifier-side pair of [`absorb_protocol_map_prover`]. Must be called with -/// the identical `names` slice on the same transcript position. -pub fn absorb_protocol_map_verifier(verifier_state: &mut VerifierState, names: &[&'static str]) { - verifier_state.public_message(b"WARP.protocol_map"); - for name in names { - verifier_state.public_message(name.as_bytes()); - verifier_state.public_message(b"|"); - } -} - -/// Destructuring carrier for [`IOR::prove`]. -pub struct IorProveResult { - pub reduced: R, - pub proof: P, - pub witness: W, -} - -/// Destructuring carrier for [`IOR::verify`]. -pub struct IorVerifyResult { - pub reduced: R, - pub outputs: V, -} - -/// Call `IOR::prove` and package the `(reduced, proof, witness)` tuple -/// into a named-field [`IorProveResult`]. -#[macro_export] -macro_rules! prove_ior { - ( - $ior:expr, - $transcript:expr, - statement: $statement:expr, - witness: $witness:expr, - inputs: $inputs:expr $(,)? - ) => {{ - let __stmt = $statement; - let __wit = $witness; - let __ins = $inputs; - $crate::protocol::ior::IOR::prove(&$ior, $transcript, &__stmt, &__wit, &__ins).map( - |(reduced, proof, witness)| $crate::protocol::ior::IorProveResult { - reduced, - proof, - witness, - }, - ) - }}; -} - -/// Call `IOR::verify` and package the `(reduced, outputs)` tuple into a -/// named-field [`IorVerifyResult`]. -#[macro_export] -macro_rules! verify_ior { - ( - $ior:expr, - $transcript:expr, - statement: $statement:expr, - inputs: $inputs:expr $(,)? - ) => {{ - let __stmt = $statement; - let __ins = $inputs; - $crate::protocol::ior::IOR::verify(&$ior, $transcript, &__stmt, &__ins) - .map(|(reduced, outputs)| $crate::protocol::ior::IorVerifyResult { reduced, outputs }) - }}; -} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs deleted file mode 100644 index 1c195b9..0000000 --- a/src/protocol/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod ior; -pub mod iors; -pub mod oracles; -pub mod transcript; diff --git a/src/protocol/oracles/indexed.rs b/src/protocol/oracles/indexed.rs deleted file mode 100644 index 99ec523..0000000 --- a/src/protocol/oracles/indexed.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! Pre-validated lookup view of an opened indexed oracle. -//! -//! With the trait migration, opening proofs live in the spongefish -//! transcript and `V::check_multiple` consumes them at a specific -//! point in the transcript. The orchestrator validates upstream and -//! hands IORs a `ValidatedOracle` — a sorted (index, value) table the -//! IOR queries without re-running the BCS check. - -use ark_ff::Field; - -pub trait IndexedOracle { - fn query(&self, i: usize) -> Option; - fn validate(&self) -> bool { - self.query(0).is_some() - } -} - -pub struct ValidatedOracle { - pub sorted_indices: Vec, - pub values_by_index: Vec>, -} - -impl ValidatedOracle { - pub fn new(sorted_indices: Vec, values_by_index: Vec>) -> Self { - Self { - sorted_indices, - values_by_index, - } - } -} - -impl IndexedOracle> for ValidatedOracle { - fn query(&self, i: usize) -> Option> { - let pos = self.sorted_indices.binary_search(&i).ok()?; - Some(self.values_by_index[pos].clone()) - } - - fn validate(&self) -> bool { - true - } -} diff --git a/src/protocol/transcript/mod.rs b/src/protocol/transcript/mod.rs deleted file mode 100644 index fffeacb..0000000 --- a/src/protocol/transcript/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -pub mod prover; -pub mod verifier; - -pub use prover::*; -pub use verifier::*; - -use effsc::transcript::VerifierTranscript; -use spongefish::{Decoding, Encoding, NargDeserialize, VerifierState}; - -/// Adapter wrapping spongefish's [`VerifierState`] so it implements effsc's -/// [`VerifierTranscript`] trait. Used to plug warp's transcript into -/// [`effsc::verifier::sumcheck_verify`]. -/// -/// The prover-side adapter is a blanket impl inside effsc -/// (`ProverTranscript` on `spongefish::ProverState`); no wrapper needed there. -pub struct EffscVerifierTranscript<'s, 'a>(pub &'s mut VerifierState<'a>); - -impl<'s, 'a, F> VerifierTranscript for EffscVerifierTranscript<'s, 'a> -where - F: ark_ff::Field + Encoding<[u8]> + Decoding<[u8]> + NargDeserialize, -{ - type Error = spongefish::VerificationError; - - fn receive(&mut self) -> Result { - self.0.prover_message::() - } - - fn challenge(&mut self) -> F { - self.0.verifier_message::() - } -} diff --git a/src/protocol/transcript/prover.rs b/src/protocol/transcript/prover.rs deleted file mode 100644 index 0091497..0000000 --- a/src/protocol/transcript/prover.rs +++ /dev/null @@ -1,60 +0,0 @@ -use ark_ff::Field; -use ark_vc::mvc::MultiVectorCommitment; -use spongefish::{Encoding, ProverState}; - -use crate::warp::AccumulatorInstance; - -// absorb a list of plain instances into the transcript -pub fn absorb_instances>( - prover_state: &mut ProverState, - instances: &[Vec], -) { - for instance in instances { - for f in instance { - prover_state.prover_message(f); - } - } -} - -// absorb an AccumulatorInstance into the transcript -impl AccumulatorInstance -where - F: Field + Encoding<[u8]>, - V: MultiVectorCommitment, - V::Commitment: Encoding<[u8]> + spongefish::NargSerialize, -{ - pub fn absorb_into(&self, prover_state: &mut ProverState) { - for commitment in &self.rt_commitments { - prover_state.prover_message(commitment); - } - - for alpha in &self.alpha_fold_vectors { - for f in alpha { - prover_state.prover_message(f); - } - } - - for f in &self.mu_claimed_evals { - prover_state.prover_message(f); - } - - // Layout: all τ vectors first (l2 of them), then all x vectors. - // Verifier reads in the same order. Keeping the τ/x halves in - // separate passes lets the verifier reconstruct the pair list - // without needing length-prefixes per pair. - for pair in &self.beta_twin_pairs { - for f in &pair.tau { - prover_state.prover_message(f); - } - } - for pair in &self.beta_twin_pairs { - for f in &pair.x { - prover_state.prover_message(f); - } - } - - for f in &self.eta_predicate_evals { - prover_state.prover_message(f); - } - } -} diff --git a/src/relations/mod.rs b/src/relations/mod.rs index d15ac83..283b2c1 100644 --- a/src/relations/mod.rs +++ b/src/relations/mod.rs @@ -5,7 +5,7 @@ pub use description::SerializableConstraintMatrices; use ark_ff::Field; -use crate::error::WARPError; +use crate::error::WarpError; pub trait Relation { type Instance; @@ -24,7 +24,7 @@ pub trait Relation { pub trait PolyPredicate { type Config; - fn evaluate_bundled(&self, zero_evader_evals: &[F], z: &[F]) -> Result; + fn evaluate_bundled(&self, zero_evader_evals: &[F], z: &[F]) -> Result; fn config(&self) -> Self::Config; fn description(&self) -> Vec; fn constraints(&self) -> &r1cs::R1CSConstraints; @@ -33,5 +33,5 @@ pub trait PolyPredicate { pub trait Arithmetize { type Config; type Predicate; - fn arithmetize(config: &Self::Config) -> Result; + fn arithmetize(config: &Self::Config) -> Result; } diff --git a/src/relations/r1cs/hashchain/mod.rs b/src/relations/r1cs/hashchain/mod.rs index d6402e5..c94c535 100644 --- a/src/relations/r1cs/hashchain/mod.rs +++ b/src/relations/r1cs/hashchain/mod.rs @@ -21,7 +21,7 @@ pub use synthesizer::HashChainSynthesizer; pub use witness::HashChainWitness; use super::R1CS; -use crate::error::WARPError; +use crate::error::WarpError; use crate::relations::Arithmetize; impl< @@ -33,7 +33,7 @@ impl< type Config = (H::Parameters, usize); type Predicate = R1CS; - fn arithmetize(config: &Self::Config) -> Result, WARPError> { + fn arithmetize(config: &Self::Config) -> Result, WarpError> { let (params, hash_chain_size) = config; let preimage = vec![F::ZERO]; let digest = compute_hash_chain::(params, &preimage, *hash_chain_size); diff --git a/src/relations/r1cs/mod.rs b/src/relations/r1cs/mod.rs index 2060d96..201acee 100644 --- a/src/relations/r1cs/mod.rs +++ b/src/relations/r1cs/mod.rs @@ -4,7 +4,7 @@ use ark_ff::Field; use ark_relations::gr1cs::ConstraintSystemRef; use rayon::prelude::*; -use crate::error::WARPError; +use crate::error::WarpError; use crate::relations::SerializableConstraintMatrices; use super::PolyPredicate; @@ -25,23 +25,23 @@ pub struct R1CS { } impl TryFrom> for R1CS { - type Error = WARPError; + type Error = WarpError; fn try_from(cs: ConstraintSystemRef) -> Result { use ark_relations::gr1cs::R1CS_PREDICATE_LABEL; - let inner = cs.into_inner().ok_or(WARPError::R1CSConstruction { + let inner = cs.into_inner().ok_or(WarpError::R1CSConstruction { reason: "constraint system has outstanding borrows", })?; let all_matrices = inner .to_matrices() - .map_err(|_| WARPError::R1CSConstruction { + .map_err(|_| WarpError::R1CSConstruction { reason: "constraint system not finalized or matrices unavailable", })?; let r1cs_matrices = all_matrices .get(R1CS_PREDICATE_LABEL) - .ok_or(WARPError::R1CSConstruction { + .ok_or(WarpError::R1CSConstruction { reason: "R1CS predicate not present in constraint system", })?; @@ -54,7 +54,7 @@ impl TryFrom> for R1CS { let n = inner.num_instance_variables() + inner.num_witness_variables(); let k = inner.num_witness_variables(); if n == 0 { - return Err(WARPError::R1CSConstruction { + return Err(WarpError::R1CSConstruction { reason: "n = num_instance_variables + num_witness_variables must be > 0", }); } @@ -89,22 +89,22 @@ impl TryFrom> for R1CS { impl R1CS { // evaluate the given sparse linear combination over the provided z vector - fn eval_lc(lc: &[(F, usize)], z: &[F]) -> Result { + fn eval_lc(lc: &[(F, usize)], z: &[F]) -> Result { let mut acc = F::zero(); for (coeff, var) in lc.iter() { acc += *coeff * z.get(*var) - .ok_or(WARPError::R1CSWitnessSize(z.len(), *var))?; + .ok_or(WarpError::R1CSWitnessSize(z.len(), *var))?; } Ok(acc) } // eval the R1CS i-th linear combination, where i is represented as an hypercube point - pub fn eval_p_i(&self, z: &[F], i: usize) -> Result { + pub fn eval_p_i(&self, z: &[F], i: usize) -> Result { let (a_i, b_i, c_i) = self .constraints_vec .get(i) - .ok_or(WARPError::R1CSNonExistingLC)?; + .ok_or(WarpError::R1CSNonExistingLC)?; let eval_a_i = Self::eval_lc(a_i, z)?; let eval_b_i = Self::eval_lc(b_i, z)?; let eval_c_i = Self::eval_lc(c_i, z)?; @@ -115,16 +115,16 @@ impl R1CS { impl PolyPredicate for R1CS { type Config = (usize, usize, usize); - fn evaluate_bundled(&self, zero_evader_evals: &[F], z: &[F]) -> Result { + fn evaluate_bundled(&self, zero_evader_evals: &[F], z: &[F]) -> Result { if zero_evader_evals.len() < self.m_num_constraints { - return Err(WARPError::ZeroEvaderSize( + return Err(WarpError::ZeroEvaderSize( zero_evader_evals.len(), self.m_num_constraints - 1, )); } (0..self.m_num_constraints) .into_par_iter() - .map(|i| -> Result { + .map(|i| -> Result { let p_i = self.eval_p_i(z, i)?; Ok(zero_evader_evals[i] * p_i) }) @@ -143,7 +143,7 @@ impl PolyPredicate for R1CS { // Serializes the *concrete matrix triple* so two R1CS systems with // identical (m, n, k) but different constraints absorb to different // bytes (and therefore distinct transcripts). Without this, - // `WARP::index` would only commit to dimensions — a soundness hole + // `WarpAccumulationScheme::index` would only commit to dimensions — a soundness hole // in any downstream protocol that trusts `index()` to bind the // relation. let a: Vec> = self diff --git a/src/serialize.rs b/src/serialize.rs index 8d494eb..47365af 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -2,9 +2,9 @@ use ark_ff::Field; use ark_serialize::{CanonicalSerialize, Compress, SerializationError, Valid, Write}; use ark_vc::mvc::MultiVectorCommitment; -use crate::warp::{AccumulatorInstance, AccumulatorWitness, WARPProof}; +use crate::accumulation_scheme::{AccumulatorInstance, AccumulatorWitness, WarpProof}; -// `AccumulatorInstance` and `WARPProof` carry generic associated types +// `AccumulatorInstance` and `WarpProof` carry generic associated types // (`V::Commitment`) whose serializability isn't implied by the trait // itself. Putting the bound in a separate `impl` block (rather than on // the struct) keeps the bulk of the IOR / orchestrator code free of that @@ -59,7 +59,7 @@ where } } -impl CanonicalSerialize for WARPProof +impl CanonicalSerialize for WarpProof where F: Field + CanonicalSerialize, V: MultiVectorCommitment, @@ -92,7 +92,7 @@ where } } -impl Valid for WARPProof +impl Valid for WarpProof where F: Field + CanonicalSerialize, V: MultiVectorCommitment, diff --git a/src/warp/mod.rs b/src/warp/mod.rs deleted file mode 100644 index 09f535f..0000000 --- a/src/warp/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! WARP accumulation scheme: the choreography that composes Warp's IORs. - -pub mod accumulator; -pub mod decide; -pub mod keys; -pub mod params; -pub mod proof; -pub mod prove; -pub mod scheme; -pub mod verify; - -pub use accumulator::{AccumulatorInstance, AccumulatorWitness}; -pub use keys::{WARPProverKey, WARPVerifierKey}; -pub use params::WARPParams; -pub use proof::{ProveResult, WARPProof}; -pub use scheme::WARP; diff --git a/tests/integration_warp.rs b/tests/integration_warp.rs index 8d3ab46..9045e61 100644 --- a/tests/integration_warp.rs +++ b/tests/integration_warp.rs @@ -1,4 +1,4 @@ -//! End-to-end integration tests for the full WARP prove / verify / decide +//! End-to-end integration tests for the full WarpAccumulationScheme prove / verify / decide //! cycle. Previously lived in `src/lib.rs` as an inline `#[cfg(test)]` //! module; moved here so `src/lib.rs` stays focused on the orchestrator. //! @@ -27,7 +27,11 @@ use ark_serialize::{CanonicalSerialize, Compress}; use ark_std::rand::thread_rng; use ark_vc::{mvc::MultiVectorCommitment, vc::VectorCommitment}; -use warp::config::WARPConfig; +use warp::accumulation_scheme::AccumulationScheme; +use warp::accumulation_scheme::{ + AccumulatorInstance, AccumulatorWitness, WarpProverKey, WarpVerifierKey, +}; +use warp::config::WarpConfig; use warp::relations::{ r1cs::{ hashchain::{compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness}, @@ -37,8 +41,7 @@ use warp::relations::{ }; use warp::serialize::acc_witness_size; use warp::utils::poseidon; -use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProverKey, WARPVerifierKey}; -use warp::WARP; +use warp::WarpAccumulationScheme; /// Concrete Merkle scheme used by the tests below. Plain perfect-binary /// tree with a Blake3 field-symbol hasher — no caps, no multi-region. @@ -108,15 +111,14 @@ fn warp_test() { )) .unwrap(); - let warp_config = WARPConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); + let warp_config = WarpConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); let (ck, vk) = build_keys::(code.code_len(), t); - let hash_chain_warp = WARP::, _, MerkleVc>::new( - warp_config.clone(), - code.clone(), - r1cs.clone(), - ck, - vk, - ); + let hash_chain_warp = WarpAccumulationScheme::< + BLS12_381, + R1CS, + _, + MerkleVc, + >::new(warp_config.clone(), code.clone(), r1cs.clone(), ck, vk); let mut acc_x = AccumulatorInstance::empty(); let mut acc_w = AccumulatorWitness::empty(); @@ -126,7 +128,7 @@ fn warp_test() { let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((new_x, new_w), _pf) = hash_chain_warp .prove( - WARPProverKey { + WarpProverKey { index: r1cs.clone(), m_num_constraints: r1cs.m_num_constraints, n_num_variables: r1cs.n_num_variables, @@ -145,21 +147,20 @@ fn warp_test() { let domainsep = spongefish::domain_separator!("test::warp"); let warp_config = - WARPConfig::<_, R1CS>::new(l1, 4, s, t, r1cs.config(), code.code_len()); + WarpConfig::<_, R1CS>::new(l1, 4, s, t, r1cs.config(), code.code_len()); let (ck, vk) = build_keys::(code.code_len(), t); - let hash_chain_warp = WARP::, _, MerkleVc>::new( - warp_config.clone(), - code.clone(), - r1cs.clone(), - ck, - vk, - ); + let hash_chain_warp = WarpAccumulationScheme::< + BLS12_381, + R1CS, + _, + MerkleVc, + >::new(warp_config.clone(), code.clone(), r1cs.clone(), ck, vk); let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), pf) = hash_chain_warp .prove( - WARPProverKey { + WarpProverKey { index: r1cs.clone(), m_num_constraints: r1cs.m_num_constraints, n_num_variables: r1cs.n_num_variables, @@ -181,7 +182,7 @@ fn warp_test() { .std_verifier(&narg_str); hash_chain_warp .verify( - WARPVerifierKey { + WarpVerifierKey { m_num_constraints: r1cs.m_num_constraints, n_num_variables: r1cs.n_num_variables, k_num_witness_vars: r1cs.k_num_witness_vars, @@ -191,9 +192,7 @@ fn warp_test() { pf.clone(), ) .unwrap(); - hash_chain_warp - .decide(acc_w.clone(), acc_x.clone()) - .unwrap(); + hash_chain_warp.decide(&acc_x, &acc_w).unwrap(); println!("acc_x size: {}", acc_x.serialized_size(Compress::Yes)); println!("acc_w size: {}", acc_witness_size(&acc_w, Compress::Yes)); @@ -248,15 +247,14 @@ fn warp_test_goldilocks() { )) .unwrap(); - let warp_config = WARPConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); + let warp_config = WarpConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); let (ck, vk) = build_keys::(code.code_len(), t); - let hash_chain_warp = WARP::, _, MerkleVc>::new( - warp_config.clone(), - code.clone(), - r1cs.clone(), - ck, - vk, - ); + let hash_chain_warp = WarpAccumulationScheme::< + Goldilocks, + R1CS, + _, + MerkleVc, + >::new(warp_config.clone(), code.clone(), r1cs.clone(), ck, vk); let mut acc_x = AccumulatorInstance::empty(); let mut acc_w = AccumulatorWitness::empty(); @@ -266,7 +264,7 @@ fn warp_test_goldilocks() { let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((new_x, new_w), _pf) = hash_chain_warp .prove( - WARPProverKey { + WarpProverKey { index: r1cs.clone(), m_num_constraints: r1cs.m_num_constraints, n_num_variables: r1cs.n_num_variables, @@ -286,21 +284,20 @@ fn warp_test_goldilocks() { let domainsep = spongefish::domain_separator!("test::warp"); // Use 8 (2*l1) for the total accumulation size to test multi-instance accumulation let warp_config = - WARPConfig::<_, R1CS>::new(l1, 4, s, t, r1cs.config(), code.code_len()); + WarpConfig::<_, R1CS>::new(l1, 4, s, t, r1cs.config(), code.code_len()); let (ck, vk) = build_keys::(code.code_len(), t); - let hash_chain_warp = WARP::, _, MerkleVc>::new( - warp_config.clone(), - code.clone(), - r1cs.clone(), - ck, - vk, - ); + let hash_chain_warp = WarpAccumulationScheme::< + Goldilocks, + R1CS, + _, + MerkleVc, + >::new(warp_config.clone(), code.clone(), r1cs.clone(), ck, vk); let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); let ((acc_x, acc_w), pf) = hash_chain_warp .prove( - WARPProverKey { + WarpProverKey { index: r1cs.clone(), m_num_constraints: r1cs.m_num_constraints, n_num_variables: r1cs.n_num_variables, @@ -322,7 +319,7 @@ fn warp_test_goldilocks() { .std_verifier(&narg_str); hash_chain_warp .verify( - WARPVerifierKey { + WarpVerifierKey { m_num_constraints: r1cs.m_num_constraints, n_num_variables: r1cs.n_num_variables, k_num_witness_vars: r1cs.k_num_witness_vars, @@ -332,9 +329,7 @@ fn warp_test_goldilocks() { pf.clone(), ) .unwrap(); - hash_chain_warp - .decide(acc_w.clone(), acc_x.clone()) - .unwrap(); + hash_chain_warp.decide(&acc_x, &acc_w).unwrap(); println!( "Goldilocks acc_x size: {}", diff --git a/tests/profile_json.rs b/tests/profile_json.rs index 621bbdb..f24acdb 100644 --- a/tests/profile_json.rs +++ b/tests/profile_json.rs @@ -19,7 +19,8 @@ use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; use ark_mt::blake3::Blake3FieldHasher; use ark_std::rand::thread_rng; use ark_std::UniformRand; -use warp::config::WARPConfig; +use warp::accumulation_scheme::{AccumulatorInstance, AccumulatorWitness, WarpProverKey}; +use warp::config::WarpConfig; use warp::relations::{ r1cs::{ hashchain::{compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness}, @@ -28,8 +29,7 @@ use warp::relations::{ Arithmetize, PolyPredicate, Relation, }; use warp::utils::poseidon; -use warp::warp::{AccumulatorInstance, AccumulatorWitness, WARPProverKey}; -use warp::WARP; +use warp::WarpAccumulationScheme; /// `Arc>>` wrapped so it implements `io::Write`. #[derive(Clone)] @@ -92,20 +92,21 @@ fn json_layer_emits_phase_records() { }) .unzip(); - let warp_config = WARPConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); - let hash_chain_warp = WARP::, _, Blake3FieldHasher>::new( - warp_config, - code, - r1cs.clone(), - Blake3FieldHasher::::new(), - ); + let warp_config = WarpConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); + let hash_chain_warp = + WarpAccumulationScheme::, _, Blake3FieldHasher>::new( + warp_config, + code, + r1cs.clone(), + Blake3FieldHasher::::new(), + ); let domainsep = spongefish::domain_separator!("test::profile_json"); let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); hash_chain_warp .prove( - WARPProverKey { + WarpProverKey { index: r1cs.clone(), m_num_constraints: r1cs.m_num_constraints, n_num_variables: r1cs.n_num_variables, diff --git a/tests/snapshot_fs.rs b/tests/snapshot_fs.rs new file mode 100644 index 0000000..0bc3358 --- /dev/null +++ b/tests/snapshot_fs.rs @@ -0,0 +1,150 @@ +//! Fiat-Shamir snapshot test — regression oracle for the ark-iop extraction. +//! +//! Captures byte-identical hashes of `narg_string` (transcript) and the +//! serialized proof for a fixed-seed warp run. Any change to the transcript +//! semantics (encoding order, byte layout, missing absorb, etc.) breaks this +//! test before it corrupts a real prover. + +use ark_codes::{ + reed_solomon::{config::ReedSolomonConfig, ReedSolomon}, + traits::LinearCode, +}; +use ark_crypto_primitives::crh::poseidon::{constraints::CRHGadget, CRH}; +use ark_ff::UniformRand; +use ark_mt::{ + blake3::Blake3FieldHasher, hash_region::HashRegion, scheme::MerkleCommitment, + shape::PerfectBinary, +}; +use ark_serialize::{CanonicalSerialize, Compress}; +use ark_std::rand::{rngs::StdRng, SeedableRng}; +use ark_vc::mvc::MultiVectorCommitment; + +use warp::accumulation_scheme::{AccumulatorInstance, AccumulatorWitness, WarpProverKey}; +use warp::config::WarpConfig; +use warp::relations::{ + r1cs::{ + hashchain::{compute_hash_chain, HashChainInstance, HashChainRelation, HashChainWitness}, + R1CS, + }, + Arithmetize, PolyPredicate, Relation, +}; +use warp::utils::{fields::Goldilocks, poseidon}; +use warp::WarpAccumulationScheme; + +type F = Goldilocks; +type VC = MerkleCommitment>, PerfectBinary>; + +fn hex_hash(bytes: &[u8]) -> String { + blake3::hash(bytes).to_hex().to_string() +} + +/// After capturing initial values, set these to `Some(...)`. Until then the +/// test prints the observed values and skips assertion — useful for the +/// first run after upgrading dependencies. +// Bumped on AccumulationScheme prologue replacing IOP prologue at the +// orchestrator level. Adds `AS:WarpAccumulationScheme-AccScheme|` prefix + the inner IOP +// NAME (`WarpAccumulationScheme|`) before the IOR list — 32 extra bytes vs the prior +// IOP-only prologue. +const EXPECTED_NARG_HASH: Option<&str> = + Some("83e5f65136d0ab5571a4f4c49e254a6bc178a264def7fba256cbdbd444b0f80d"); +const EXPECTED_NARG_LEN: Option = Some(1616); +const EXPECTED_PROOF_HASH: Option<&str> = + Some("68aec437a44825f513f3b5bfa55049e2c466b533bb0aede65dc63c6f76b7ec9c"); +const EXPECTED_PROOF_LEN: Option = Some(224); + +#[test] +fn fs_transcript_snapshot_goldilocks() { + let mut rng = StdRng::seed_from_u64(0xFA1A_F542); + // Compact but representative: exercises all 5 IORs at least once. + let l1 = 2; + let s = 2; + let t = 4; + let hash_chain_size = 4; + + let poseidon_config = poseidon::initialize_poseidon_config::(); + let r1cs = HashChainRelation::, CRHGadget<_>>::arithmetize(&( + poseidon_config.clone(), + hash_chain_size, + )) + .unwrap(); + + let code = ReedSolomon::new(ReedSolomonConfig::::default( + r1cs.k_num_witness_vars, + r1cs.k_num_witness_vars.next_power_of_two(), + )); + + let instances_witnesses: (Vec>, Vec>) = (0..l1) + .map(|_| { + let preimage = vec![F::rand(&mut rng)]; + let instance = HashChainInstance { + digest: compute_hash_chain::>( + &poseidon_config, + &preimage, + hash_chain_size, + ), + }; + let witness = HashChainWitness::>::new(preimage); + let rel = HashChainRelation::, CRHGadget<_>>::new( + instance, + witness, + (poseidon_config.clone(), hash_chain_size), + ); + (rel.x, rel.w) + }) + .unzip(); + + let warp_config = WarpConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); + let pp = ::setup_multiple(0, code.code_len(), t, &mut rng) + .expect("setup_multiple"); + let (ck, vk) = ::trim_multiple(&pp, 0, code.code_len(), t) + .expect("trim_multiple"); + let warp_inst = + WarpAccumulationScheme::, _, VC>::new(warp_config, code, r1cs.clone(), ck, vk); + + let domainsep = spongefish::domain_separator!("test::snapshot"); + let mut prover_state = domainsep.without_session().instance(&0u32).std_prover(); + let (_acc, pf) = warp_inst + .prove( + WarpProverKey { + index: r1cs.clone(), + m_num_constraints: r1cs.m_num_constraints, + n_num_variables: r1cs.n_num_variables, + k_num_witness_vars: r1cs.k_num_witness_vars, + }, + &mut prover_state, + instances_witnesses.1, + instances_witnesses.0, + AccumulatorInstance::empty(), + AccumulatorWitness::empty(), + ) + .unwrap(); + + let narg = prover_state.narg_string().to_vec(); + let mut pf_bytes = Vec::new(); + pf.serialize_with_mode(&mut pf_bytes, Compress::Yes) + .unwrap(); + + let narg_hash = hex_hash(&narg); + let pf_hash = hex_hash(&pf_bytes); + + println!( + "fs_snapshot:\n narg_len = {}\n narg_hash = {}\n proof_len = {}\n proof_hash = {}", + narg.len(), + narg_hash, + pf_bytes.len(), + pf_hash, + ); + + if let Some(expected) = EXPECTED_NARG_LEN { + assert_eq!(narg.len(), expected, "narg_string length changed"); + } + if let Some(expected) = EXPECTED_NARG_HASH { + assert_eq!(narg_hash, expected, "Fiat-Shamir transcript drift detected"); + } + if let Some(expected) = EXPECTED_PROOF_LEN { + assert_eq!(pf_bytes.len(), expected, "proof serialized length changed"); + } + if let Some(expected) = EXPECTED_PROOF_HASH { + assert_eq!(pf_hash, expected, "proof bytes changed"); + } +} diff --git a/tests/verifier_negative.rs b/tests/verifier_negative.rs index 8504f13..6864497 100644 --- a/tests/verifier_negative.rs +++ b/tests/verifier_negative.rs @@ -34,7 +34,10 @@ use ark_std::rand::thread_rng; use ark_std::UniformRand; use ark_vc::{mvc::MultiVectorCommitment, vc::VectorCommitment}; -use warp::config::WARPConfig; +use warp::accumulation_scheme::{ + AccumulatorInstance, AccumulatorWitness, WarpProof, WarpProverKey, WarpVerifierKey, +}; +use warp::config::WarpConfig; use warp::error::VerifierError; use warp::relations::{ r1cs::{ @@ -44,14 +47,11 @@ use warp::relations::{ Arithmetize, PolyPredicate, Relation, }; use warp::utils::poseidon; -use warp::warp::{ - AccumulatorInstance, AccumulatorWitness, WARPProof, WARPProverKey, WARPVerifierKey, -}; -use warp::WARP; +use warp::WarpAccumulationScheme; type F = BLS12_381; type V = MerkleCommitment>, PerfectBinary>; -type WarpT = WARP, ReedSolomon, V>; +type WarpT = WarpAccumulationScheme, ReedSolomon, V>; fn build_keys( code_len: usize, @@ -71,9 +71,9 @@ fn build_keys( /// to re-derive the verifier state. struct Fixture { warp: WarpT, - vk: WARPVerifierKey, + vk: WarpVerifierKey, acc_x: AccumulatorInstance, - proof: WARPProof, + proof: WarpProof, narg_str: Vec, } @@ -81,7 +81,7 @@ impl Fixture { fn verify( &self, acc_x: AccumulatorInstance, - proof: WARPProof, + proof: WarpProof, ) -> Result<(), VerifierError> { let domainsep_v = spongefish::domain_separator!("test::warp::negative"); let mut verifier_state = domainsep_v @@ -134,9 +134,15 @@ fn make_fixture() -> Fixture { // Phase 1: produce `l1` single-round acc states so we have a non-trivial // accumulator to feed phase 2 (l2 > 0 so NumL2Instances is reachable). - let warp_cfg1 = WARPConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); + let warp_cfg1 = WarpConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); let (ck1, vk1) = build_keys(code.code_len(), t); - let w1 = WARP::, _, V>::new(warp_cfg1, code.clone(), r1cs.clone(), ck1, vk1); + let w1 = WarpAccumulationScheme::, _, V>::new( + warp_cfg1, + code.clone(), + r1cs.clone(), + ck1, + vk1, + ); let mut acc_x = AccumulatorInstance::empty(); let mut acc_w = AccumulatorWitness::empty(); @@ -146,7 +152,7 @@ fn make_fixture() -> Fixture { let mut ps = ds.without_session().instance(&0u32).std_prover(); let ((new_x, new_w), _) = w1 .prove( - WARPProverKey { + WarpProverKey { index: r1cs.clone(), m_num_constraints: r1cs.m_num_constraints, n_num_variables: r1cs.n_num_variables, @@ -164,15 +170,16 @@ fn make_fixture() -> Fixture { } // Phase 2: the "real" prove with l2 > 0 accumulated instances. - let warp_cfg2 = WARPConfig::<_, R1CS>::new(l1, 4, s, t, r1cs.config(), code.code_len()); + let warp_cfg2 = WarpConfig::<_, R1CS>::new(l1, 4, s, t, r1cs.config(), code.code_len()); let (ck2, vk2) = build_keys(code.code_len(), t); - let warp = WARP::, _, V>::new(warp_cfg2, code, r1cs.clone(), ck2, vk2); + let warp = + WarpAccumulationScheme::, _, V>::new(warp_cfg2, code, r1cs.clone(), ck2, vk2); let ds = spongefish::domain_separator!("test::warp::negative"); let mut ps = ds.without_session().instance(&0u32).std_prover(); let ((acc_x, _acc_w), proof) = warp .prove( - WARPProverKey { + WarpProverKey { index: r1cs.clone(), m_num_constraints: r1cs.m_num_constraints, n_num_variables: r1cs.n_num_variables, @@ -188,7 +195,7 @@ fn make_fixture() -> Fixture { Fixture { warp, - vk: WARPVerifierKey { + vk: WarpVerifierKey { m_num_constraints: r1cs.m_num_constraints, n_num_variables: r1cs.n_num_variables, k_num_witness_vars: r1cs.k_num_witness_vars, @@ -271,7 +278,7 @@ fn tampered_shift_query_answer_raises_target() { // `truncated_auth_j_raises_num_l2_instances` removed: opening proofs now // live in the spongefish transcript (written by `V::open_multiple`), so -// there are no longer separate `auth_j` fields on `WARPProof` to truncate. +// there are no longer separate `auth_j` fields on `WarpProof` to truncate. // Equivalent coverage would require transcript-byte tampering, which is a // spongefish-layer concern, not warp's. @@ -324,9 +331,10 @@ fn prove_rejects_mismatched_instance_witness_lengths() { }) .unzip(); - let warp_cfg = WARPConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); + let warp_cfg = WarpConfig::new(l1, 0, s, t, r1cs.config(), code.code_len()); let (ck, vk) = build_keys(code.code_len(), t); - let warp = WARP::, _, V>::new(warp_cfg, code, r1cs.clone(), ck, vk); + let warp = + WarpAccumulationScheme::, _, V>::new(warp_cfg, code, r1cs.clone(), ck, vk); let mut witnesses_short = witnesses; witnesses_short.pop(); @@ -334,7 +342,7 @@ fn prove_rejects_mismatched_instance_witness_lengths() { let ds = spongefish::domain_separator!("test::warp::prove_negative"); let mut ps = ds.without_session().instance(&0u32).std_prover(); let result = warp.prove( - WARPProverKey { + WarpProverKey { index: r1cs.clone(), m_num_constraints: r1cs.m_num_constraints, n_num_variables: r1cs.n_num_variables, From 4db2bd3d8269d941d010c55f91224ad411635044 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Thu, 14 May 2026 19:47:15 +0200 Subject: [PATCH 68/68] fix CI --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 865c194..ee42e44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ tracing = { version = "0.1", default-features = false, features = ["std", "attri tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "env-filter", "registry"], optional = true } libc = { version = "0.2", optional = true } ark-codes = { git = "https://github.com/z-tech/ark-codes.git", branch = "z-tech/arkworks-0.6.0" } -ark-iop = { git = "https://github.com/arkworks-rs/z.git", branch = "z-tech/ark-iop" } +ark-iop = { git = "https://github.com/arkworks-rs/ark-vc.git", branch = "z-tech/ark-iop" } ark-vc = { git = "https://github.com/arkworks-rs/ark-vc.git", branch = "z-tech/hash-regions" } ark-mt = { git = "https://github.com/arkworks-rs/ark-vc.git", branch = "z-tech/hash-regions", features = ["blake3", "blake3-field", "vc"] }

I+gA% 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 diff --git a/docs/paper-mods/slides/iors.snm b/docs/paper-mods/slides/iors.snm deleted file mode 100644 index e69de29..0000000 diff --git a/docs/paper-mods/slides/iors.tex b/docs/paper-mods/slides/iors.tex index 47b3ba5..bfadae4 100644 --- a/docs/paper-mods/slides/iors.tex +++ b/docs/paper-mods/slides/iors.tex @@ -254,7 +254,7 @@ \item \textbf{Transcripts.} \texttt{spongefish} 0.7 \,---\, de facto in this corner of the ecosystem. Practitioners use it directly. No abstraction needed. \item \textbf{Sumchecks.} \texttt{effsc} \,---\, \texttt{SumcheckProver}, \texttt{RoundPolyEvaluator}, \texttt{InnerProductProver}, \texttt{CoefficientProverLSB}, \texttt{MultilinearProver}, hypercube + protogalaxy utilities. WARP uses 9 of 10 effsc primitives already. \item \textbf{Linear codes.} \texttt{ark-codes::LinearCode} \,---\, encode, code\_len. - \item \textbf{Merkle commitments.} \texttt{ark-crypto-primitives::merkle\_tree} \,---\, generic over leaf shape, hashes. + \item \textbf{Vector commitments.} \texttt{ark-vc} (abstract trait) + \texttt{ark-mt} (Merkle backend, multi-vector + single-shot openings, Blake3 / Poseidon hashers). \item \textbf{Oracle struct.} \texttt{Vec} codeword + lazy MLE cache. Same storage shape across STIR / WHIR / WARP. \end{itemize} @@ -278,27 +278,29 @@ \end{frame} % -------------------------------------------------------------------- -\begin{frame}[fragile]{The one new abstraction: \texttt{CodeCommitment}} -The single integration gap not already covered by the toolkit: +\begin{frame}[fragile]{The integration trait already exists: \texttt{ark-vc}} +The single integration gap, now filled by \texttt{ark-vc} + \texttt{ark-mt}: \begin{lstlisting} -pub trait CodeCommitment { - type Commitment; - type Opening; - - fn commit(&self, codeword: &[F]) -> Self::Commitment; - fn open_index(&self, codeword: &[F], i: usize) -> Self::Opening; - fn verify_opening( - &self, c: &Self::Commitment, i: usize, val: F, o: &Self::Opening, - ) -> bool; +pub trait MerkleCommitment { + fn commit(&self, msg: &[H::Symbol]) -> Committed<...>; + fn open(&self, td: &Committed<...>, indices: &[usize]) + -> OpeningProof<...>; + fn check(&self, root: &H::Digest, + opening: &Opening, + proof: &OpeningProof<...>) -> bool; } + +// Multi-vector variant: one root over m interleaved codewords, +// one OpeningProof for arbitrary-many indices. WARP uses this. +pub struct MultiVectorMerkleCommitment { ... } \end{lstlisting} \vspace{0.4em} -Default impl: \texttt{LinearCode} + Merkle config from \texttt{ark-crypto-primitives}. \begin{itemize}\small - \item Wraps the only integration practitioners currently re-roll per protocol. - \item Small (one trait, one default impl), focused, validated by WARP migrating onto it. + \item Single multi-opening proof per commitment (fewer auth paths than per-index). + \item Backend-agnostic: \texttt{Blake3FieldHasher}, Poseidon hasher, hybrid stacks. + \item WARP migrated onto it: \texttt{Path} $\to$ \texttt{OpeningProof>}; \texttt{MerkleTree} $\to$ \texttt{MultiVectorCommitted}; \texttt{H: MerkleHasher} replaces \texttt{MT: Config} across phases. 33/33 tests still green. \end{itemize} \end{frame} @@ -312,11 +314,11 @@ \textbf{Lean on the existing primitive layer.} \begin{itemize}\small - \item \texttt{effsc}, \texttt{spongefish}, \texttt{ark-codes}, \texttt{ark-crypto-primitives::merkle\_tree}. + \item \texttt{effsc}, \texttt{spongefish}, \texttt{ark-codes}, \texttt{ark-vc} + \texttt{ark-mt}. \item Already strong. The discipline is to use them, not reinvent. \end{itemize} -\textbf{Add \texttt{CodeCommitment}.} The one missing integration trait. WARP migrates onto it; STIR-lite plugs into it. +\textbf{WARP runs on \texttt{ark-vc}.} The missing integration trait was filled by \texttt{ark-vc}/\texttt{ark-mt}; WARP migrated onto its multi-vector commitment scheme. STIR-lite plugs into the same surface. \textbf{Make WARP the reference implementation.} A new protocol designer reads \texttt{src/protocol/phases/}, copies the IOR-shaped pattern, plugs in their primitives. @@ -331,7 +333,7 @@ \textbf{Paper section (structured sumcheck primitive).} The fused $\alpha/\beta/\tau$-fold inside TwinConstraint is the natural extraction \,---\, lives in \texttt{effsc} as a new \texttt{RoundPolyEvaluator}-shaped impl, citable from outside Warp. \vspace{0.4em} -\textbf{Add \texttt{CodeCommitment}} \,---\, small focused trait, ships in a session. Migrate WARP onto it. +\textbf{\texttt{ark-vc} integration landed.} WARP migrated from \texttt{ark-crypto-primitives::merkle\_tree} onto \texttt{ark-vc}/\texttt{ark-mt}. Multi-vector openings collapse PESAT's $l_1$ paths to one proof per query batch. Tests + benches green. \vspace{0.4em} \textbf{Implement a second protocol.} STIR-lite or WHIR-lite, against the same primitives + IOR-shaped pattern. The friction it surfaces is the most reliable guide for the next iteration. diff --git a/docs/paper-mods/slides/iors.toc b/docs/paper-mods/slides/iors.toc deleted file mode 100644 index e69de29..0000000 diff --git a/docs/paper-mods/slides/iors.vrb b/docs/paper-mods/slides/iors.vrb deleted file mode 100644 index f3b76af..0000000 --- a/docs/paper-mods/slides/iors.vrb +++ /dev/null @@ -1,22 +0,0 @@ -\frametitle{The one new abstraction: \texttt {CodeCommitment}} -The single integration gap not already covered by the toolkit: - -\begin{lstlisting} -pub trait CodeCommitment { - type Commitment; - type Opening; - - fn commit(&self, codeword: &[F]) -> Self::Commitment; - fn open_index(&self, codeword: &[F], i: usize) -> Self::Opening; - fn verify_opening( - &self, c: &Self::Commitment, i: usize, val: F, o: &Self::Opening, - ) -> bool; -} -\end{lstlisting} - -\vspace{0.4em} -Default impl: \texttt{LinearCode} + Merkle config from \texttt{ark-crypto-primitives}. -\begin{itemize}\small - \item Wraps the only integration practitioners currently re-roll per protocol. - \item Small (one trait, one default impl), focused, validated by WARP migrating onto it. -\end{itemize} diff --git a/src/crypto/merkle/mod.rs b/src/crypto/merkle/mod.rs index 9330bf7..fae02c1 100644 --- a/src/crypto/merkle/mod.rs +++ b/src/crypto/merkle/mod.rs @@ -1,36 +1,45 @@ +//! Warp's vector-commitment layer, built on `ark-vc` / `ark-mt`. +//! +//! Replaces the previous `ark-crypto-primitives::merkle_tree`-based +//! implementation. Warp commits to L1 codewords interleaved into one +//! Merkle tree (PESAT phase) and to single folded codewords across the +//! accumulator. Both shapes are handled by `MultiVectorMerkleCommitment` +//! (the m=1 case is a degenerate single-codeword tree). + use ark_codes::traits::LinearCode; -use ark_crypto_primitives::{ - merkle_tree::{Config, MerkleTree, Path}, - Error, -}; use ark_ff::Field; +use ark_mt::{ + layer_stack::LayerGroup, multi_vector::MultiVectorMerkleCommitment, shape::PerfectBinary, + MerkleHasher, +}; -pub fn build_codeword_leaves>( - code: &C, - witnesses: &[Vec], - l1: usize, -) -> (Vec>, Vec) { - let mut leaves = vec![F::default(); l1 * code.code_len()]; - let mut codewords = vec![vec![F::default(); code.code_len()]; l1]; - for (i, w) in witnesses.iter().enumerate() { - let f_i = code.encode(w); - // stacking codewords in flat array, which we chunk below - // [[w_0[0], .., w_{N-1}[0]], .., [w_0[N-1], .., w_{N-1}[N-1]]] // L * N elements - for (j, value) in f_i.iter().enumerate() { - leaves[(j * l1) + i] = *value; - } - codewords[i] = f_i; - } - (codewords, leaves) +/// Warp's commitment scheme: any field-symbol hasher over a +/// power-of-two binary tree. +pub type WarpScheme = MultiVectorMerkleCommitment; + +/// Output of a commit: the codewords + state needed to open at any index. +pub type WarpCommitted = ark_mt::multi_vector::MultiVectorCommitted; + +/// Verifier-side opening (indices + per-codeword opened values). +pub type WarpOpening = ark_mt::multi_vector::MultiVectorOpening; + +/// Authentication paths for the opened indices. +pub type WarpProof = ark_mt::OpeningProof>; + +/// Build a `WarpScheme` for a given hasher and codeword length. +pub fn warp_scheme(hasher: H, code_len: usize) -> WarpScheme +where + H: MerkleHasher>, + F: Field + Clone, +{ + MultiVectorMerkleCommitment::new(hasher, PerfectBinary::with_num_leaves(code_len)) } -pub fn compute_auth_paths( - td: &MerkleTree