Skip to content

Commit a0d8cda

Browse files
Move component log sizes from proof claim to Statement
The Statement trait now exposes get_component_log_sizes; verify reads log sizes from the statement instead of proof.claim. The Claim struct is removed (claimed_sums is now a top-level field on Proof), shrinking the serialized proof by one packed-u8 entry per component. CircuitStatement derives the per-component log sizes from n_blake_compress (added to CircuitParams/CircuitConfig) plus preprocessed column log sizes, and now takes &CircuitConfig instead of seven individual fields. Cairo's component log sizes ride along on public_claim, removing the dedicated field on CairoVerifierConfig. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 96ac738 commit a0d8cda

21 files changed

Lines changed: 212 additions & 196 deletions

File tree

crates/cairo_verifier/src/privacy_test.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ fn verify_circuit_proof(
4646
config: circuit_proof.pcs_config,
4747
output_addresses: preprocessed_circuit.params.output_addresses.clone(),
4848
n_blake_gates: preprocessed_circuit.params.n_blake_gates,
49+
n_blake_compress: preprocessed_circuit.params.n_blake_compress,
4950
preprocessed_column_ids: preprocessed_circuit.preprocessed_trace.ids(),
5051
preprocessed_column_log_sizes: preprocessed_circuit.preprocessed_trace.log_sizes(),
5152
preprocessed_root,
@@ -240,6 +241,7 @@ fn test_privacy_proof_info() {
240241
config: pcs_config,
241242
output_addresses: preprocessed_circuit.params.output_addresses.clone(),
242243
n_blake_gates: preprocessed_circuit.params.n_blake_gates,
244+
n_blake_compress: preprocessed_circuit.params.n_blake_compress,
243245
preprocessed_column_ids: preprocessed_circuit.preprocessed_trace.ids(),
244246
preprocessed_column_log_sizes: preprocessed_circuit.preprocessed_trace.log_sizes(),
245247
preprocessed_root,
@@ -248,15 +250,8 @@ fn test_privacy_proof_info() {
248250
output_values: vec![QM31::zero(); preprocessed_circuit.params.output_addresses.len()],
249251
};
250252
let mut context = Context::<NoValue>::default();
251-
let statement = CircuitStatement::new(
252-
&mut context,
253-
&circuit_config.output_addresses,
254-
&public_data.output_values,
255-
circuit_config.n_blake_gates,
256-
circuit_config.preprocessed_column_ids.clone(),
257-
circuit_config.preprocessed_column_log_sizes.clone(),
258-
circuit_config.preprocessed_root,
259-
);
253+
let statement =
254+
CircuitStatement::new(&mut context, &circuit_config, &public_data.output_values);
260255

261256
let enabled_bits = vec![true; all_circuit_components::<NoValue>().len()];
262257
let proof_config = ProofConfig::new(
@@ -269,5 +264,5 @@ fn test_privacy_proof_info() {
269264
let proof_info = ProofInfo::from_config(&proof_config);
270265
println!("{proof_info}");
271266
// Assert the total size in bytes.
272-
assert_eq!(proof_info.total_bytes(), 347360);
267+
assert_eq!(proof_info.total_bytes(), 347344);
273268
}

crates/cairo_verifier/src/statement.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ pub struct CairoStatement<Value: IValue> {
145145
pub packed_outputs: Simd,
146146
pub preprocessed_root: HashValue<QM31>,
147147
pub preprocessed_trace_variant: PreProcessedTraceVariant,
148+
pub component_log_sizes: Simd,
148149
}
149150

150151
impl<Value: IValue> CairoStatement<Value> {
@@ -261,21 +262,29 @@ impl<Value: IValue> CairoStatement<Value> {
261262
}
262263

263264
impl<Value: IValue> CairoStatement<Value> {
265+
/// `public_claim` is the flat public claim laid out as:
266+
/// `[public_data (PUBLIC_DATA_LEN + outputs.len() + program.len() M31s) | component_log_sizes
267+
/// (components.len() M31s)]`.
264268
pub fn new(
265269
context: &mut Context<Value>,
266-
public_data: Vec<M31>,
270+
public_claim: Vec<M31>,
267271
outputs: Vec<[M31; MEMORY_VALUES_LIMBS]>,
268272
program: Arc<[[M31; MEMORY_VALUES_LIMBS]]>,
269273
components: IndexMap<&'static str, Box<dyn CircuitEval<Value>>>,
270274
preprocessed_root: HashValue<QM31>,
271275
preprocessed_trace_variant: PreProcessedTraceVariant,
272276
) -> Self {
273-
let packed_public_data = pack_into_qm31s(public_data.iter().cloned())
277+
let n_components = components.len();
278+
let public_data_len = PUBLIC_DATA_LEN + outputs.len() + program.len();
279+
assert_eq!(public_claim.len(), public_data_len + n_components);
280+
let (public_data_m31s, log_sizes_m31s) = public_claim.split_at(public_data_len);
281+
282+
let packed_public_data = pack_into_qm31s(public_data_m31s.iter().cloned())
274283
.into_iter()
275284
.map(|qm31| Value::from_qm31(qm31).guess(context))
276285
.collect_vec();
277286

278-
let packed_public_data = Simd::from_packed(packed_public_data, public_data.len());
287+
let packed_public_data = Simd::from_packed(packed_public_data, public_data_m31s.len());
279288
// Note that we don't enforce anything on the padding M31 in packed_public_data.
280289
let unpacked_simd = Simd::unpack(context, &packed_public_data);
281290

@@ -289,12 +298,19 @@ impl<Value: IValue> CairoStatement<Value> {
289298
.collect_vec();
290299
let packed_outputs = Simd::from_packed(packed_outputs, n_outputs * MEMORY_VALUES_LIMBS);
291300

301+
let packed_log_sizes = pack_into_qm31s(log_sizes_m31s.iter().cloned())
302+
.into_iter()
303+
.map(|qm31| Value::from_qm31(qm31).guess(context))
304+
.collect_vec();
305+
let component_log_sizes = Simd::from_packed(packed_log_sizes, n_components);
306+
292307
Self {
293308
packed_public_data,
294309
public_data,
295310
program,
296311
packed_outputs,
297312
components,
313+
component_log_sizes,
298314
preprocessed_root,
299315
preprocessed_trace_variant,
300316
}
@@ -306,13 +322,18 @@ impl<Value: IValue> Statement<Value> for CairoStatement<Value> {
306322
&self.components
307323
}
308324

325+
fn get_component_log_sizes(&self) -> &Simd {
326+
&self.component_log_sizes
327+
}
328+
309329
fn claims_to_mix(&self, context: &mut Context<Value>) -> Vec<Vec<Var>> {
310330
let Self {
311331
components: _components,
312332
packed_public_data,
313333
public_data: _public_data,
314334
program,
315335
packed_outputs,
336+
component_log_sizes: _component_log_sizes,
316337
preprocessed_root: _preprocessed_root,
317338
preprocessed_trace_variant: _preprocessed_trace_variant,
318339
} = self;

crates/cairo_verifier/src/test.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub fn verify_cairo_with_component_set(
5353
cairo_proof: &CairoProof<Blake2sM31MerkleHasher>,
5454
component_set: HashSet<&str>,
5555
) -> Result<Context<QM31>, String> {
56-
let FlatClaim { component_enable_bits, component_log_sizes: _, public_data: _ } =
56+
let FlatClaim { component_enable_bits, component_log_sizes, public_data: _ } =
5757
cairo_proof.claim.flatten_claim();
5858
let components: indexmap::IndexMap<&'static str, Box<dyn CircuitEval<QM31>>> =
5959
zip_eq(all_components::<QM31>().into_iter(), &component_enable_bits)
@@ -80,7 +80,8 @@ pub fn verify_cairo_with_component_set(
8080
);
8181

8282
let (proof, public_data) = prepare_cairo_proof_for_circuit_verifier(cairo_proof, &proof_config);
83-
let (public_claim, outputs, program) = public_data.pack_into_u32s();
83+
let (mut public_claim, outputs, program) = public_data.pack_into_u32s();
84+
public_claim.extend(component_log_sizes);
8485
let outputs = outputs
8586
.chunks_exact(MEMORY_VALUES_LIMBS)
8687
.map(|chunk| array::from_fn(|i| M31::from_u32_unchecked(chunk[i])))
@@ -112,25 +113,27 @@ fn test_verify() {
112113
let mut novalue_context = Context::<NoValue>::default();
113114
let output_len = 1;
114115
let program_len = 128;
115-
let flat_claim = vec![M31::zero(); PUBLIC_DATA_LEN + output_len + program_len];
116116
let outputs = vec![[M31::zero(); MEMORY_VALUES_LIMBS]; output_len];
117117
let program: Arc<[[M31; MEMORY_VALUES_LIMBS]]> =
118118
std::iter::repeat_n([M31::zero(); MEMORY_VALUES_LIMBS], program_len).collect();
119-
let components = all_components();
120-
let mut statement = CairoStatement::new(
119+
// Remove the pedersen points table component since it requires long preprocessed columns, which
120+
// are not supported.
121+
let pedersen_points_index =
122+
all_components::<NoValue>().get_full("pedersen_points_table_window_bits_18").unwrap().0;
123+
let mut components = all_components();
124+
components.shift_remove("pedersen_points_table_window_bits_18");
125+
126+
let public_claim =
127+
vec![M31::zero(); PUBLIC_DATA_LEN + output_len + program_len + components.len()];
128+
let statement = CairoStatement::new(
121129
&mut novalue_context,
122-
flat_claim,
130+
public_claim,
123131
outputs,
124132
program,
125133
components,
126134
get_preprocessed_root(20 + pcs_config.fri_config.log_blowup_factor),
127135
PreProcessedTraceVariant::CanonicalSmall,
128136
);
129-
// Remove the pedersen points table component since it requires long preprocessed columns, which
130-
// are not supported.
131-
let pedersen_points_index =
132-
all_components::<NoValue>().get_full("pedersen_points_table_window_bits_18").unwrap().0;
133-
statement.components.shift_remove("pedersen_points_table_window_bits_18");
134137

135138
let mut enabled_bits = vec![true; all_components::<NoValue>().len()];
136139
enabled_bits[pedersen_points_index] = false;

crates/cairo_verifier/src/verify.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ use circuits::context::{Context, TraceContext};
1010
use circuits::ivalue::{IValue, NoValue};
1111
use circuits::ops::Guess;
1212
use circuits_stark_verifier::constraint_eval::CircuitEval;
13-
use circuits_stark_verifier::proof::{Claim, Proof, ProofConfig, empty_proof};
14-
use circuits_stark_verifier::proof_from_stark_proof::{
15-
pack_component_log_sizes, proof_from_stark_proof,
16-
};
13+
use circuits_stark_verifier::proof::{Proof, ProofConfig, empty_proof};
14+
use circuits_stark_verifier::proof_from_stark_proof::proof_from_stark_proof;
1715
use circuits_stark_verifier::verify::verify;
1816
use indexmap::IndexMap;
1917
use itertools::{Itertools, zip_eq};
@@ -139,13 +137,14 @@ pub fn build_cairo_verifier_circuit(verifier_config: &CairoVerifierConfig) -> Co
139137

140138
let n_outputs = verifier_config.n_outputs;
141139
let program_len = verifier_config.program.len();
142-
let public_data = vec![M31::zero(); PUBLIC_DATA_LEN + n_outputs + program_len];
140+
let n_components = components.len();
141+
let public_claim = vec![M31::zero(); PUBLIC_DATA_LEN + n_outputs + program_len + n_components];
143142
let outputs = vec![[M31::zero(); MEMORY_VALUES_LIMBS]; n_outputs];
144143

145144
let mut context = Context::<NoValue>::default();
146145
let statement = CairoStatement::<NoValue>::new(
147146
&mut context,
148-
public_data,
147+
public_claim,
149148
outputs,
150149
verifier_config.program.clone(),
151150
components,
@@ -181,15 +180,10 @@ pub fn prepare_cairo_proof_for_circuit_verifier(
181180
debug_assert_eq!(component_log_sizes.len(), proof_config.n_components());
182181
debug_assert_eq!(claimed_sums.len(), proof_config.n_components());
183182

184-
let claim = Claim {
185-
packed_component_log_sizes: pack_component_log_sizes(&component_log_sizes),
186-
claimed_sums,
187-
};
188-
189183
let proof = proof_from_stark_proof(
190184
extended_stark_proof,
191185
proof_config,
192-
claim,
186+
claimed_sums,
193187
*interaction_pow,
194188
*channel_salt,
195189
);

crates/circuit_common/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ pub struct CircuitParams {
99
pub trace_log_size: u32,
1010
pub first_permutation_row: usize,
1111
pub n_blake_gates: usize,
12+
/// Total number of blake compression blocks across all blake gates (unpadded). Equals
13+
/// `sum(gate.input.len())`.
14+
pub n_blake_compress: usize,
1215
pub output_addresses: Vec<usize>,
1316
}
1417

crates/circuit_common/src/preprocessed.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,10 +556,12 @@ impl PreprocessedCircuit {
556556
let blake_g_log_size = log_n_blake_updates + 7;
557557
let trace_log_size = std::cmp::max(max_pp_trace_log_size, blake_g_log_size);
558558

559+
let n_blake_compress: usize = circuit.blake.iter().map(|gate| gate.input.len()).sum();
559560
let params = CircuitParams {
560561
trace_log_size,
561562
first_permutation_row: qm31_ops_trace_generator.first_permutation_row,
562563
n_blake_gates: circuit.blake.len(),
564+
n_blake_compress,
563565
output_addresses: circuit.output.iter().map(|out| out.in0).collect(),
564566
};
565567

crates/circuit_prover/src/prover.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@ use circuit_verifier::circuit_claim::{
1111
use circuit_verifier::statement::INTERACTION_POW_BITS;
1212
use circuit_verifier::verify::CircuitPublicData;
1313
use circuits_stark_verifier::proof::Proof;
14-
use circuits_stark_verifier::proof::{Claim, ProofConfig};
15-
use circuits_stark_verifier::proof_from_stark_proof::{
16-
pack_component_log_sizes, proof_from_stark_proof,
17-
};
14+
use circuits_stark_verifier::proof::ProofConfig;
15+
use circuits_stark_verifier::proof_from_stark_proof::proof_from_stark_proof;
1816
use itertools::chain;
1917
use num_traits::Zero;
2018
use stwo::core::air::Component;
@@ -263,15 +261,12 @@ pub fn prepare_circuit_proof_for_circuit_verifier(
263261

264262
let public_data = CircuitPublicData { output_values: claim.output_values.clone() };
265263

266-
let claim = Claim {
267-
packed_component_log_sizes: pack_component_log_sizes(&claim.log_sizes),
268-
claimed_sums: interaction_claim.claimed_sums.to_vec(),
269-
};
264+
let claimed_sums = interaction_claim.claimed_sums.to_vec();
270265

271266
let proof = proof_from_stark_proof(
272267
&stark_proof,
273268
proof_config,
274-
claim,
269+
claimed_sums,
275270
interaction_pow_nonce,
276271
channel_salt,
277272
);

crates/circuit_prover/src/prover_test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ fn circuit_verify(
251251
config: circuit_proof.pcs_config,
252252
output_addresses: preprocessed_circuit.params.output_addresses.clone(),
253253
n_blake_gates: preprocessed_circuit.params.n_blake_gates,
254+
n_blake_compress: preprocessed_circuit.params.n_blake_compress,
254255
preprocessed_column_ids: preprocessed_circuit.preprocessed_trace.ids(),
255256
preprocessed_column_log_sizes: preprocessed_circuit.preprocessed_trace.log_sizes(),
256257
preprocessed_root: preprocessed_root.into(),

crates/circuit_serialize/src/deserialize.rs

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ use circuits_stark_verifier::fri_proof::{
1010
};
1111
use circuits_stark_verifier::merkle::{AuthPath, AuthPaths};
1212
use circuits_stark_verifier::oods::{EvalDomainSamples, N_COMPOSITION_COLUMNS};
13-
use circuits_stark_verifier::proof::{Claim, InteractionAtOods, N_TRACES, Proof, ProofConfig};
14-
use circuits_stark_verifier::proof_from_stark_proof::pack_component_log_sizes;
13+
use circuits_stark_verifier::proof::{InteractionAtOods, N_TRACES, Proof, ProofConfig};
1514

1615
#[derive(Debug)]
1716
pub enum DeserializeError {
@@ -105,7 +104,7 @@ pub fn deserialize_proof_with_config(
105104
let trace_root = HashValue::<QM31>::deserialize(data)?;
106105
let interaction_root = HashValue::<QM31>::deserialize(data)?;
107106
let composition_polynomial_root = HashValue::<QM31>::deserialize(data)?;
108-
let claim = deserialize_claim(data, config)?;
107+
let claimed_sums = deserialize_vec(data, config.n_components())?;
109108
let preprocessed_columns_at_oods = deserialize_vec(data, config.n_preprocessed_columns())?;
110109
let trace_at_oods = deserialize_vec(data, config.n_trace_columns)?;
111110
let interaction_at_oods = deserialize_interaction_at_oods(data, config)?;
@@ -124,7 +123,7 @@ pub fn deserialize_proof_with_config(
124123
preprocessed_columns_at_oods,
125124
trace_at_oods,
126125
composition_eval_at_oods,
127-
claim,
126+
claimed_sums,
128127
interaction_at_oods,
129128
eval_domain_samples,
130129
eval_domain_auth_paths,
@@ -134,19 +133,6 @@ pub fn deserialize_proof_with_config(
134133
})
135134
}
136135

137-
fn deserialize_claim(data: &mut &[u8], config: &ProofConfig) -> DeserializeResult<Claim<QM31>> {
138-
let n_components = config.n_components();
139-
140-
// Unpack log sizes from packed u8s (1 per u8, 8 bits each).
141-
let log_sizes: Vec<u32> =
142-
take_bytes(data, n_components.next_multiple_of(4))?.iter().map(|&b| b as u32).collect();
143-
let packed_component_log_sizes = pack_component_log_sizes(&log_sizes);
144-
145-
let claimed_sums = deserialize_vec(data, n_components)?;
146-
147-
Ok(Claim { packed_component_log_sizes, claimed_sums })
148-
}
149-
150136
fn deserialize_interaction_at_oods(
151137
data: &mut &[u8],
152138
config: &ProofConfig,

crates/circuit_serialize/src/serialize.rs

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use circuits::wrappers::M31Wrapper;
66
use circuits_stark_verifier::fri_proof::{FriCommitProof, FriProof, FriWitness};
77
use circuits_stark_verifier::merkle::{AuthPath, AuthPaths};
88
use circuits_stark_verifier::oods::EvalDomainSamples;
9-
use circuits_stark_verifier::proof::{Claim, InteractionAtOods, Proof};
10-
use circuits_stark_verifier::verify::LOG_SIZE_BITS;
9+
use circuits_stark_verifier::proof::{InteractionAtOods, Proof};
1110

1211
pub trait CircuitSerialize {
1312
fn serialize(&self, output: &mut Vec<u8>);
@@ -23,7 +22,7 @@ impl CircuitSerialize for Proof<QM31> {
2322
preprocessed_columns_at_oods,
2423
trace_at_oods,
2524
composition_eval_at_oods,
26-
claim,
25+
claimed_sums,
2726
interaction_at_oods,
2827
eval_domain_samples,
2928
eval_domain_auth_paths,
@@ -36,7 +35,7 @@ impl CircuitSerialize for Proof<QM31> {
3635
trace_root.serialize(output);
3736
interaction_root.serialize(output);
3837
composition_polynomial_root.serialize(output);
39-
claim.serialize(output);
38+
claimed_sums.serialize(output);
4039
preprocessed_columns_at_oods.as_slice().serialize(output);
4140
trace_at_oods.as_slice().serialize(output);
4241
interaction_at_oods.as_slice().serialize(output);
@@ -89,22 +88,6 @@ impl CircuitSerialize for HashValue<QM31> {
8988
}
9089
}
9190

92-
impl CircuitSerialize for Claim<QM31> {
93-
fn serialize(&self, output: &mut Vec<u8>) {
94-
let Self { packed_component_log_sizes, claimed_sums } = self;
95-
96-
// Pack log sizes: 1 per u8 (requires `LOG_SIZE_BITS` <= 8).
97-
assert!(LOG_SIZE_BITS as usize <= 8);
98-
for qm31 in packed_component_log_sizes {
99-
for m31 in qm31.to_m31_array().into_iter() {
100-
output.push((m31.0 & 0xFF) as u8);
101-
}
102-
}
103-
104-
claimed_sums.serialize(output);
105-
}
106-
}
107-
10891
impl CircuitSerialize for InteractionAtOods<QM31> {
10992
fn serialize(&self, output: &mut Vec<u8>) {
11093
let Self { at_oods, at_prev } = self;

0 commit comments

Comments
 (0)