Skip to main content

binius_core/constraint_system/
values_data.rs

1// Copyright 2025 Irreducible Inc.
2use std::borrow::Cow;
3
4use binius_utils::serialization::{DeserializeBytes, SerializationError, SerializeBytes};
5use bytes::{Buf, BufMut};
6
7use crate::word::Word;
8
9/// Values data for zero-knowledge proofs (either public witness or non-public part - private inputs
10/// and internal values).
11///
12/// It uses `Cow<[Word]>` to avoid unnecessary clones while supporting
13/// both borrowed and owned data.
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub struct ValuesData<'a> {
16	data: Cow<'a, [Word]>,
17}
18
19impl<'a> ValuesData<'a> {
20	/// Serialization format version for compatibility checking
21	pub const SERIALIZATION_VERSION: u32 = 1;
22
23	/// Create a new ValuesData from borrowed data
24	pub const fn borrowed(data: &'a [Word]) -> Self {
25		Self {
26			data: Cow::Borrowed(data),
27		}
28	}
29
30	/// Create a new ValuesData from owned data
31	pub const fn owned(data: Vec<Word>) -> Self {
32		Self {
33			data: Cow::Owned(data),
34		}
35	}
36
37	/// Get the values data as a slice
38	pub fn as_slice(&self) -> &[Word] {
39		&self.data
40	}
41
42	/// Get the number of words in the values data
43	pub fn len(&self) -> usize {
44		self.data.len()
45	}
46
47	/// Check if the witness is empty
48	pub fn is_empty(&self) -> bool {
49		self.data.is_empty()
50	}
51
52	/// Convert to owned data, consuming self
53	pub fn into_owned(self) -> Vec<Word> {
54		self.data.into_owned()
55	}
56
57	/// Convert to owned version of ValuesData
58	pub fn to_owned(&self) -> ValuesData<'static> {
59		ValuesData {
60			data: Cow::Owned(self.data.to_vec()),
61		}
62	}
63}
64
65impl<'a> SerializeBytes for ValuesData<'a> {
66	fn serialize(&self, mut write_buf: impl BufMut) -> Result<(), SerializationError> {
67		Self::SERIALIZATION_VERSION.serialize(&mut write_buf)?;
68
69		self.data.as_ref().serialize(write_buf)
70	}
71}
72
73impl DeserializeBytes for ValuesData<'static> {
74	fn deserialize(mut read_buf: impl Buf) -> Result<Self, SerializationError>
75	where
76		Self: Sized,
77	{
78		let version = u32::deserialize(&mut read_buf)?;
79		if version != Self::SERIALIZATION_VERSION {
80			return Err(SerializationError::InvalidConstruction {
81				name: "Witness::version",
82			});
83		}
84
85		let data = Vec::<Word>::deserialize(read_buf)?;
86
87		Ok(ValuesData::owned(data))
88	}
89}
90
91impl<'a> From<&'a [Word]> for ValuesData<'a> {
92	fn from(data: &'a [Word]) -> Self {
93		ValuesData::borrowed(data)
94	}
95}
96
97impl From<Vec<Word>> for ValuesData<'static> {
98	fn from(data: Vec<Word>) -> Self {
99		ValuesData::owned(data)
100	}
101}
102
103impl<'a> AsRef<[Word]> for ValuesData<'a> {
104	fn as_ref(&self) -> &[Word] {
105		self.as_slice()
106	}
107}
108
109impl<'a> std::ops::Deref for ValuesData<'a> {
110	type Target = [Word];
111
112	fn deref(&self) -> &Self::Target {
113		self.as_slice()
114	}
115}
116
117impl<'a> From<ValuesData<'a>> for Vec<Word> {
118	fn from(value: ValuesData<'a>) -> Self {
119		value.into_owned()
120	}
121}
122
123#[cfg(test)]
124mod tests {
125	use super::*;
126
127	#[test]
128	fn test_witness_serialization_round_trip_owned() {
129		let data = vec![
130			Word::from_u64(1),
131			Word::from_u64(42),
132			Word::from_u64(0xDEADBEEF),
133			Word::from_u64(0x1234567890ABCDEF),
134		];
135		let witness = ValuesData::owned(data.clone());
136
137		let mut buf = Vec::new();
138		witness.serialize(&mut buf).unwrap();
139
140		let deserialized = ValuesData::deserialize(&mut buf.as_slice()).unwrap();
141		assert_eq!(witness, deserialized);
142		assert_eq!(deserialized.as_slice(), data.as_slice());
143	}
144
145	#[test]
146	fn test_witness_serialization_round_trip_borrowed() {
147		let data = vec![Word::from_u64(123), Word::from_u64(456)];
148		let witness = ValuesData::borrowed(&data);
149
150		let mut buf = Vec::new();
151		witness.serialize(&mut buf).unwrap();
152
153		let deserialized = ValuesData::deserialize(&mut buf.as_slice()).unwrap();
154		assert_eq!(witness, deserialized);
155		assert_eq!(deserialized.as_slice(), data.as_slice());
156	}
157
158	#[test]
159	fn test_witness_version_mismatch() {
160		let mut buf = Vec::new();
161		999u32.serialize(&mut buf).unwrap(); // Wrong version
162		vec![Word::from_u64(1)].serialize(&mut buf).unwrap(); // Some data
163
164		let result = ValuesData::deserialize(&mut buf.as_slice());
165		assert!(result.is_err());
166		match result.unwrap_err() {
167			SerializationError::InvalidConstruction { name } => {
168				assert_eq!(name, "Witness::version");
169			}
170			_ => panic!("Expected version mismatch error"),
171		}
172	}
173
174	/// Helper function to create or update the reference binary file for Witness version
175	/// compatibility testing.
176	#[test]
177	#[ignore] // Use `cargo test -- --ignored create_witness_reference_binary` to run this
178	fn create_witness_reference_binary_file() {
179		let data = vec![
180			Word::from_u64(1),
181			Word::from_u64(42),
182			Word::from_u64(0xDEADBEEF),
183			Word::from_u64(0x1234567890ABCDEF),
184		];
185		let witness = ValuesData::owned(data);
186
187		let mut buf = Vec::new();
188		witness.serialize(&mut buf).unwrap();
189
190		let test_data_path = std::path::Path::new("verifier/core/test_data/witness_v1.bin");
191
192		if let Some(parent) = test_data_path.parent() {
193			std::fs::create_dir_all(parent).unwrap();
194		}
195
196		std::fs::write(test_data_path, &buf).unwrap();
197
198		println!("Created Witness reference binary file at: {:?}", test_data_path);
199		println!("Binary data length: {} bytes", buf.len());
200	}
201
202	/// Test deserialization from a reference binary file to ensure Witness version
203	/// compatibility. This test will fail if breaking changes are made without incrementing the
204	/// version.
205	#[test]
206	fn test_witness_deserialize_from_reference_binary_file() {
207		let binary_data = include_bytes!("../../test_data/witness_v1.bin");
208
209		let deserialized = ValuesData::deserialize(&mut binary_data.as_slice()).unwrap();
210
211		assert_eq!(deserialized.len(), 4);
212		assert_eq!(deserialized.as_slice()[0].as_u64(), 1);
213		assert_eq!(deserialized.as_slice()[1].as_u64(), 42);
214		assert_eq!(deserialized.as_slice()[2].as_u64(), 0xDEADBEEF);
215		assert_eq!(deserialized.as_slice()[3].as_u64(), 0x1234567890ABCDEF);
216
217		// Verify that the version is what we expect
218		// This is implicitly checked during deserialization, but we can also verify
219		// the file starts with the correct version bytes
220		let version_bytes = &binary_data[0..4]; // First 4 bytes should be version
221		let expected_version_bytes = 1u32.to_le_bytes(); // Version 1 in little-endian
222		assert_eq!(
223			version_bytes, expected_version_bytes,
224			"WitnessData binary file version mismatch. If you made breaking changes, increment WitnessData::SERIALIZATION_VERSION"
225		);
226	}
227}