binius_core/constraint_system/
proof.rs1use std::borrow::Cow;
3
4use binius_utils::serialization::{DeserializeBytes, SerializationError, SerializeBytes};
5use bytes::{Buf, BufMut};
6
7#[derive(Clone, Debug, PartialEq, Eq)]
24pub struct Proof<'a> {
25 data: Cow<'a, [u8]>,
26 challenger_type: String,
27}
28
29impl<'a> Proof<'a> {
30 pub const SERIALIZATION_VERSION: u32 = 1;
32
33 pub const fn borrowed(data: &'a [u8], challenger_type: String) -> Self {
35 Self {
36 data: Cow::Borrowed(data),
37 challenger_type,
38 }
39 }
40
41 pub const fn owned(data: Vec<u8>, challenger_type: String) -> Self {
43 Self {
44 data: Cow::Owned(data),
45 challenger_type,
46 }
47 }
48
49 pub fn as_slice(&self) -> &[u8] {
51 &self.data
52 }
53
54 pub fn challenger_type(&self) -> &str {
56 &self.challenger_type
57 }
58
59 pub fn len(&self) -> usize {
61 self.data.len()
62 }
63
64 pub fn is_empty(&self) -> bool {
66 self.data.is_empty()
67 }
68
69 pub fn into_owned(self) -> (Vec<u8>, String) {
71 (self.data.into_owned(), self.challenger_type)
72 }
73
74 pub fn to_owned(&self) -> Proof<'static> {
76 Proof {
77 data: Cow::Owned(self.data.to_vec()),
78 challenger_type: self.challenger_type.clone(),
79 }
80 }
81}
82
83impl<'a> SerializeBytes for Proof<'a> {
84 fn serialize(&self, mut write_buf: impl BufMut) -> Result<(), SerializationError> {
85 Self::SERIALIZATION_VERSION.serialize(&mut write_buf)?;
86
87 self.challenger_type.serialize(&mut write_buf)?;
88
89 self.data.as_ref().serialize(write_buf)
90 }
91}
92
93impl DeserializeBytes for Proof<'static> {
94 fn deserialize(mut read_buf: impl Buf) -> Result<Self, SerializationError>
95 where
96 Self: Sized,
97 {
98 let version = u32::deserialize(&mut read_buf)?;
99 if version != Self::SERIALIZATION_VERSION {
100 return Err(SerializationError::InvalidConstruction {
101 name: "Proof::version",
102 });
103 }
104
105 let challenger_type = String::deserialize(&mut read_buf)?;
106 let data = Vec::<u8>::deserialize(read_buf)?;
107
108 Ok(Proof::owned(data, challenger_type))
109 }
110}
111
112impl<'a> From<(&'a [u8], String)> for Proof<'a> {
113 fn from((data, challenger_type): (&'a [u8], String)) -> Self {
114 Proof::borrowed(data, challenger_type)
115 }
116}
117
118impl From<(Vec<u8>, String)> for Proof<'static> {
119 fn from((data, challenger_type): (Vec<u8>, String)) -> Self {
120 Proof::owned(data, challenger_type)
121 }
122}
123
124impl<'a> AsRef<[u8]> for Proof<'a> {
125 fn as_ref(&self) -> &[u8] {
126 self.as_slice()
127 }
128}
129
130impl<'a> std::ops::Deref for Proof<'a> {
131 type Target = [u8];
132
133 fn deref(&self) -> &Self::Target {
134 self.as_slice()
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use rand::prelude::*;
141
142 use super::*;
143
144 #[test]
145 fn test_proof_serialization_round_trip_owned() {
146 let transcript_data = vec![0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
147 let challenger_type = "HasherChallenger<Sha256>".to_string();
148 let proof = Proof::owned(transcript_data.clone(), challenger_type.clone());
149
150 let mut buf = Vec::new();
151 proof.serialize(&mut buf).unwrap();
152
153 let deserialized = Proof::deserialize(&mut buf.as_slice()).unwrap();
154 assert_eq!(proof, deserialized);
155 assert_eq!(deserialized.as_slice(), transcript_data.as_slice());
156 assert_eq!(deserialized.challenger_type(), &challenger_type);
157 }
158
159 #[test]
160 fn test_proof_serialization_round_trip_borrowed() {
161 let transcript_data = vec![0xAA, 0xBB, 0xCC, 0xDD];
162 let challenger_type = "TestChallenger".to_string();
163 let proof = Proof::borrowed(&transcript_data, challenger_type.clone());
164
165 let mut buf = Vec::new();
166 proof.serialize(&mut buf).unwrap();
167
168 let deserialized = Proof::deserialize(&mut buf.as_slice()).unwrap();
169 assert_eq!(proof, deserialized);
170 assert_eq!(deserialized.as_slice(), transcript_data.as_slice());
171 assert_eq!(deserialized.challenger_type(), &challenger_type);
172 }
173
174 #[test]
175 fn test_proof_empty_transcript() {
176 let proof = Proof::owned(vec![], "EmptyProof".to_string());
177 assert!(proof.is_empty());
178 assert_eq!(proof.len(), 0);
179
180 let mut buf = Vec::new();
181 proof.serialize(&mut buf).unwrap();
182
183 let deserialized = Proof::deserialize(&mut buf.as_slice()).unwrap();
184 assert_eq!(proof, deserialized);
185 assert!(deserialized.is_empty());
186 }
187
188 #[test]
189 fn test_proof_large_transcript() {
190 let mut rng = StdRng::seed_from_u64(12345);
191 let mut large_data = vec![0u8; 10000];
192 rng.fill_bytes(&mut large_data);
193
194 let challenger_type = "LargeProofChallenger".to_string();
195 let proof = Proof::owned(large_data.clone(), challenger_type.clone());
196
197 let mut buf = Vec::new();
198 proof.serialize(&mut buf).unwrap();
199
200 let deserialized = Proof::deserialize(&mut buf.as_slice()).unwrap();
201 assert_eq!(proof, deserialized);
202 assert_eq!(deserialized.len(), 10000);
203 assert_eq!(deserialized.challenger_type(), &challenger_type);
204 }
205
206 #[test]
207 fn test_proof_version_mismatch() {
208 let mut buf = Vec::new();
209 999u32.serialize(&mut buf).unwrap(); "TestChallenger".serialize(&mut buf).unwrap(); vec![0xAAu8].serialize(&mut buf).unwrap(); let result = Proof::deserialize(&mut buf.as_slice());
214 assert!(result.is_err());
215 match result.unwrap_err() {
216 SerializationError::InvalidConstruction { name } => {
217 assert_eq!(name, "Proof::version");
218 }
219 _ => panic!("Expected version mismatch error"),
220 }
221 }
222
223 #[test]
224 fn test_proof_into_owned() {
225 let original_data = vec![1, 2, 3, 4, 5];
226 let original_challenger = "TestChallenger".to_string();
227 let proof = Proof::owned(original_data.clone(), original_challenger.clone());
228
229 let (data, challenger_type) = proof.into_owned();
230 assert_eq!(data, original_data);
231 assert_eq!(challenger_type, original_challenger);
232 }
233
234 #[test]
235 fn test_proof_to_owned() {
236 let data = vec![0xFF, 0xEE, 0xDD];
237 let challenger_type = "BorrowedChallenger".to_string();
238 let borrowed_proof = Proof::borrowed(&data, challenger_type.clone());
239
240 let owned_proof = borrowed_proof.to_owned();
241 assert_eq!(owned_proof.as_slice(), data);
242 assert_eq!(owned_proof.challenger_type(), &challenger_type);
243 drop(data); assert_eq!(owned_proof.len(), 3);
246 }
247
248 #[test]
249 fn test_proof_different_challenger_types() {
250 let data = vec![0x42];
251 let challengers = vec![
252 "HasherChallenger<Sha256>".to_string(),
253 "HasherChallenger<Blake2b>".to_string(),
254 "CustomChallenger".to_string(),
255 "".to_string(), ];
257
258 for challenger_type in challengers {
259 let proof = Proof::owned(data.clone(), challenger_type.clone());
260 let mut buf = Vec::new();
261 proof.serialize(&mut buf).unwrap();
262
263 let deserialized = Proof::deserialize(&mut buf.as_slice()).unwrap();
264 assert_eq!(deserialized.challenger_type(), &challenger_type);
265 }
266 }
267
268 #[test]
269 fn test_proof_serialization_with_different_sources() {
270 let transcript_data = vec![0x11, 0x22, 0x33, 0x44];
271 let challenger_type = "MultiSourceChallenger".to_string();
272 let original = Proof::owned(transcript_data, challenger_type);
273
274 let mut vec_buf = Vec::new();
276 original.serialize(&mut vec_buf).unwrap();
277 let deserialized1 = Proof::deserialize(&mut vec_buf.as_slice()).unwrap();
278 assert_eq!(original, deserialized1);
279
280 let mut bytes_buf = bytes::BytesMut::new();
282 original.serialize(&mut bytes_buf).unwrap();
283 let deserialized2 = Proof::deserialize(bytes_buf.freeze()).unwrap();
284 assert_eq!(original, deserialized2);
285 }
286
287 #[test]
290 #[ignore] fn create_proof_reference_binary_file() {
292 let transcript_data = vec![
293 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54,
294 0x32, 0x10, 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE,
295 ];
296 let challenger_type = "HasherChallenger<Sha256>".to_string();
297 let proof = Proof::owned(transcript_data, challenger_type);
298
299 let mut buf = Vec::new();
300 proof.serialize(&mut buf).unwrap();
301
302 let test_data_path = std::path::Path::new("verifier/core/test_data/proof_v1.bin");
303
304 if let Some(parent) = test_data_path.parent() {
305 std::fs::create_dir_all(parent).unwrap();
306 }
307
308 std::fs::write(test_data_path, &buf).unwrap();
309
310 println!("Created Proof reference binary file at: {:?}", test_data_path);
311 println!("Binary data length: {} bytes", buf.len());
312 }
313
314 #[test]
318 fn test_proof_deserialize_from_reference_binary_file() {
319 let binary_data = include_bytes!("../../test_data/proof_v1.bin");
320
321 let deserialized = Proof::deserialize(&mut binary_data.as_slice()).unwrap();
322
323 assert_eq!(deserialized.len(), 24); assert_eq!(deserialized.challenger_type(), "HasherChallenger<Sha256>");
325
326 let expected_data = vec![
327 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54,
328 0x32, 0x10, 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE,
329 ];
330 assert_eq!(deserialized.as_slice(), expected_data);
331
332 let version_bytes = &binary_data[0..4]; let expected_version_bytes = 1u32.to_le_bytes(); assert_eq!(
338 version_bytes, expected_version_bytes,
339 "Proof binary file version mismatch. If you made breaking changes, increment Proof::SERIALIZATION_VERSION"
340 );
341 }
342}