use lib_q_stark_baby_bear::BabyBear;
use lib_q_stark_commit::{
BatchOpeningRef,
Mmcs,
};
use lib_q_stark_field::{
Field,
PrimeCharacteristicRing,
};
use lib_q_stark_matrix::Dimensions;
use lib_q_stark_matrix::dense::RowMajorMatrix;
use lib_q_stark_merkle::MerkleTreeMmcs;
use lib_q_stark_shake128::Shake128Hash;
use lib_q_stark_symmetric::{
CompressionFunctionFromHasher,
SerializingHasher,
};
type Val = BabyBear;
type MyHash = SerializingHasher<Shake128Hash>;
type MyCompress = CompressionFunctionFromHasher<Shake128Hash, 2, 32>;
type ValMmcs = MerkleTreeMmcs<<Val as Field>::Packing, u8, MyHash, MyCompress, 32>;
fn mmcs() -> ValMmcs {
ValMmcs::new(MyHash::new(Shake128Hash), MyCompress::new(Shake128Hash))
}
fn matrix(height: usize, width: usize) -> RowMajorMatrix<Val> {
RowMajorMatrix::new(
(0..height * width)
.map(|i| Val::new(i as u32 + 1))
.collect(),
width,
)
}
#[test]
fn honest_opening_is_accepted_at_every_row() {
let mmcs = mmcs();
let height = 8;
let width = 3;
let (commitment, prover_data) = mmcs.commit_matrix(matrix(height, width));
let dims = [Dimensions { width, height }];
for index in 0..height {
let opening = mmcs.open_batch(index, &prover_data);
mmcs.verify_batch(&commitment, &dims, index, BatchOpeningRef::from(&opening))
.unwrap_or_else(|e| panic!("honest opening at index {index} rejected: {e:?}"));
}
}
#[test]
fn tampered_opened_value_is_rejected() {
let mmcs = mmcs();
let height = 8;
let width = 3;
let (commitment, prover_data) = mmcs.commit_matrix(matrix(height, width));
let dims = [Dimensions { width, height }];
let index = 3;
let mut tampered = mmcs.open_batch(index, &prover_data);
tampered.opened_values[0][0] += Val::ONE;
let result = mmcs.verify_batch(&commitment, &dims, index, BatchOpeningRef::from(&tampered));
assert!(
result.is_err(),
"verify_batch accepted a batch opening whose leaf value was tampered with"
);
}
#[test]
fn tampered_proof_sibling_is_rejected() {
let mmcs = mmcs();
let height = 8;
let width = 3;
let (commitment, prover_data) = mmcs.commit_matrix(matrix(height, width));
let dims = [Dimensions { width, height }];
let index = 5;
let mut tampered = mmcs.open_batch(index, &prover_data);
assert!(
!tampered.opening_proof.is_empty(),
"test setup: height 8 must produce a non-empty Merkle path"
);
tampered.opening_proof[0][0] ^= 0xFF;
let result = mmcs.verify_batch(&commitment, &dims, index, BatchOpeningRef::from(&tampered));
assert!(
result.is_err(),
"verify_batch accepted a batch opening with a tampered Merkle sibling"
);
}
#[test]
fn opening_replayed_at_the_wrong_index_is_rejected() {
let mmcs = mmcs();
let height = 8;
let width = 3;
let (commitment, prover_data) = mmcs.commit_matrix(matrix(height, width));
let dims = [Dimensions { width, height }];
let opening = mmcs.open_batch(2, &prover_data);
let result = mmcs.verify_batch(&commitment, &dims, 6, BatchOpeningRef::from(&opening));
assert!(
result.is_err(),
"verify_batch accepted an honest opening for index 2 when told it was index 6"
);
}
#[test]
fn mixed_height_batch_round_trips_and_rejects_tampering() {
let mmcs = mmcs();
let tall = matrix(8, 2);
let short = matrix(2, 2);
let dims = [
Dimensions {
width: 2,
height: 8,
},
Dimensions {
width: 2,
height: 2,
},
];
let (commitment, prover_data) = mmcs.commit(vec![tall, short]);
for index in 0..8 {
let opening = mmcs.open_batch(index, &prover_data);
assert_eq!(opening.opened_values.len(), 2);
mmcs.verify_batch(&commitment, &dims, index, BatchOpeningRef::from(&opening))
.unwrap_or_else(|e| {
panic!("honest mixed-height opening at index {index} rejected: {e:?}")
});
}
let mut tampered = mmcs.open_batch(0, &prover_data);
tampered.opened_values[1][0] += Val::ONE; let result = mmcs.verify_batch(&commitment, &dims, 0, BatchOpeningRef::from(&tampered));
assert!(
result.is_err(),
"verify_batch accepted tampering in the shorter matrix of a mixed-height batch"
);
}