#[cfg(feature = "serde")]
pub mod opening {
use lens::{Commitment, Opening};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Serialize, Deserialize)]
struct TensorWire {
round_commitments: Vec<Commitment>,
final_poly: Vec<u8>,
query_indices: Vec<u32>,
}
pub fn serialize<S: Serializer>(opening: &Opening, s: S) -> Result<S::Ok, S::Error> {
let Opening::Tensor { round_commitments, final_poly, query_responses } = opening else {
return Err(serde::ser::Error::custom("zheng proof opening is Brakedown Tensor"));
};
let query_indices = query_responses
.iter()
.map(|(idx, _)| u32::try_from(*idx).map_err(serde::ser::Error::custom))
.collect::<Result<Vec<u32>, _>>()?;
TensorWire {
round_commitments: round_commitments.clone(),
final_poly: final_poly.clone(),
query_indices,
}
.serialize(s)
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Opening, D::Error> {
let w = TensorWire::deserialize(d)?;
Ok(Opening::Tensor {
round_commitments: w.round_commitments,
final_poly: w.final_poly,
query_responses: w.query_indices.into_iter().map(|i| (i as usize, Vec::new())).collect(),
})
}
}
#[cfg(all(test, feature = "serde"))]
mod tests {
use lens::Opening;
use lens::brakedown::Brakedown;
use lens::{Lens, MultilinearPoly, Transcript as LensTranscript};
use nebu::Goldilocks;
#[derive(serde::Serialize, serde::Deserialize)]
struct Holder {
#[serde(with = "super::opening")]
opening: Opening,
}
#[test]
fn wire_opening_keeps_what_the_verifier_reads() {
let poly = MultilinearPoly::new((1u64..=16).map(Goldilocks::new).collect());
let commitment = Brakedown::commit(&poly);
let point: Vec<Goldilocks> = (3u64..7).map(Goldilocks::new).collect();
let value = poly.evaluate(&point);
let mut lt = LensTranscript::new(b"wire");
let opening = Brakedown::open(&poly, &point, &mut lt);
let bytes = postcard::to_allocvec(&Holder { opening: opening.clone() }).unwrap();
let back: Holder = postcard::from_bytes(&bytes).unwrap();
let Opening::Tensor { query_responses: full, round_commitments: rc, final_poly: fp } = &opening
else {
panic!("Brakedown opening is Tensor");
};
let Opening::Tensor { query_responses: thin, round_commitments: rc2, final_poly: fp2 } =
&back.opening
else {
panic!("wire form restores a Tensor opening");
};
assert_eq!(rc, rc2);
assert_eq!(fp, fp2);
assert_eq!(full.len(), thin.len());
assert!(full.iter().zip(thin).all(|((a, _), (b, v))| a == b && v.is_empty()));
let full_bytes: usize = full.iter().map(|(_, v)| v.len()).sum();
assert_eq!(full_bytes, 4 * 20 * 8, "the symbols the wire drops: 20 queries ร 8 B per round");
let mut vt = LensTranscript::new(b"wire");
assert!(Brakedown::verify(&commitment, &point, value, &back.opening, &mut vt));
}
}