Skip to content

Commit 923f130

Browse files
Fix test to be independent of the query positions (#459)
The `WrongFriSibling` case in `test_verify` tampered with every value in the last inner FRI layer's coset and then asserted that the error contained `witness.0.last()[0][1]` — the value at a hardcoded coset index of 1. This only held when the Fiat-Shamir-driven query bits happened to pick index 1 in that coset; any change that reshuffled query positions could silently shift the failing index and break the test. Rename the variant to `WrongFriWitness` (the tampered data is the layer's witness, not just a Merkle sibling) and compute the coset index from the query position and the fold-step schedule: `selected_idx = (q >> cumulative_shift) & ((1 << last_step) - 1)`. The cumulative shift comes from the same `compute_all_fold_steps` helper that the prover-side `construct_fri_witness` uses, so the test and the proof construction stay in sync. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5c252c3 commit 923f130

1 file changed

Lines changed: 22 additions & 7 deletions

File tree

crates/stark_verifier_examples/src/verify_test.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use circuit_serialize::serialize::CircuitSerialize;
99
use circuits::context::{Context, TraceContext};
1010
use circuits::ivalue::NoValue;
1111
use circuits::ops::Guess;
12+
use circuits_stark_verifier::fri_proof::compute_all_fold_steps;
1213
use circuits_stark_verifier::proof::{ProofConfig, ProofInfo, empty_proof};
1314
use circuits_stark_verifier::proof_from_stark_proof::proof_from_stark_proof;
1415
use circuits_stark_verifier::verify::verify;
@@ -20,15 +21,15 @@ enum ProofModifier {
2021
WrongTraceAuthPath,
2122
/// Modify an element of the first layer Merkle authentication path (decommitment).
2223
WrongFriAuthPath,
23-
/// Modify the siblings in the last inner layer of FRI.
24-
WrongFriSibling,
24+
/// Modify the queried coset value at the last inner layer of FRI.
25+
WrongFriWitness,
2526
}
2627

2728
#[rstest]
2829
#[case::success(ProofModifier::None)]
2930
#[case::wrong_trace_auth_path(ProofModifier::WrongTraceAuthPath)]
3031
#[case::wrong_fri_auth_path(ProofModifier::WrongFriAuthPath)]
31-
#[case::wrong_fri_sibling(ProofModifier::WrongFriSibling)]
32+
#[case::wrong_fri_witness(ProofModifier::WrongFriWitness)]
3233
fn test_verify(#[case] proof_modifier: ProofModifier) {
3334
let (_components, claim, pcs_config, mut proof, interaction_pow_nonce, channel_salt) =
3435
create_proof();
@@ -62,14 +63,17 @@ fn test_verify(#[case] proof_modifier: ProofModifier) {
6263
first_layer_values.get_mut(&((first_query >> 1) ^ 1)).unwrap();
6364
value.0[0] ^= 1;
6465
}
65-
ProofModifier::WrongFriSibling => {
66+
ProofModifier::WrongFriWitness => {
6667
let values = &mut proof.aux.fri.inner_layers.last_mut().unwrap().all_values[0];
6768
for (_, value) in values.iter_mut() {
6869
*value += QM31::one();
6970
}
7071
}
7172
}
7273

74+
// Capture before `proof` is shadowed below; the `Proof<QM31>` has no `.aux` field.
75+
let first_query = proof.aux.unsorted_query_locations[0];
76+
7377
// Create a context with values from the proof.
7478
let mut context = TraceContext::default();
7579
let proof = proof_from_stark_proof(&proof, &config, claim, interaction_pow_nonce, channel_salt);
@@ -94,10 +98,21 @@ fn test_verify(#[case] proof_modifier: ProofModifier) {
9498
let expected_value = context.get(proof_vars.fri.commit.layer_commitments[0].0);
9599
assert!(err.contains(&expected_value.to_string()));
96100
}
97-
ProofModifier::WrongFriSibling => {
101+
ProofModifier::WrongFriWitness => {
98102
let err = result.unwrap_err();
99-
// The error should be when validating the query position inside its witness coset.
100-
let expected_value = context.get(proof_vars.fri.witness.0.last().unwrap()[0][1]);
103+
// The error should be when validating the query position inside its witness
104+
// coset. `validate_query_position_in_coset` selects the value at the query's
105+
// local index (`queried_pos`'s low `last_step` bits) in the last-layer coset.
106+
let fold_steps = compute_all_fold_steps(
107+
config.fri.log_trace_size - config.fri.log_n_last_layer_coefs,
108+
config.fri.fold_step,
109+
);
110+
let last_step = *fold_steps.last().unwrap();
111+
let shift: usize = fold_steps.iter().sum::<usize>() - last_step;
112+
let queried_pos = first_query >> shift;
113+
let index_in_coset = queried_pos & ((1 << last_step) - 1);
114+
let expected_value =
115+
context.get(proof_vars.fri.witness.0.last().unwrap()[0][index_in_coset]);
101116
assert!(err.contains(&expected_value.to_string()));
102117
}
103118
}

0 commit comments

Comments
 (0)