1use crate::{commitment::*, diff::Diff, encoding, utils::evals_to_polynomial};
13use ark_ff::PrimeField;
14use ark_poly::{univariate::DensePolynomial, EvaluationDomain, Radix2EvaluationDomain as R2D};
15use kimchi::curve::KimchiCurve;
16use poly_commitment::ipa::SRS;
17use std::{
18 fs::{File, OpenOptions},
19 io::{Read, Seek, SeekFrom, Write},
20};
21
22use crate::SRS_SIZE;
23
24pub struct Data<F: PrimeField> {
25 pub data: Vec<F>,
26}
27
28impl<F: PrimeField> Data<F> {
29 pub fn of_bytes(bytes: &[u8]) -> Data<F> {
31 Data {
32 data: encoding::encode_as_field_elements_full(bytes),
33 }
34 }
35
36 pub fn is_empty(&self) -> bool {
37 self.data.is_empty()
38 }
39
40 pub fn len(&self) -> usize {
42 self.data.len()
43 }
44
45 pub fn to_polynomial(&self, domain: R2D<F>) -> DensePolynomial<F> {
49 use std::iter;
50 let n = domain.size();
51 let padded_data: Vec<F> = self
52 .data
53 .iter()
54 .cloned()
55 .chain(iter::repeat(F::zero()))
56 .take(n)
57 .collect();
58 evals_to_polynomial(padded_data, domain)
59 }
60
61 pub fn to_commitment<G: KimchiCurve<ScalarField = F>>(&self, srs: &SRS<G>) -> Commitment<G> {
64 Commitment::from_data(srs, &self.data)
65 }
66
67 pub fn apply_inplace(&mut self, diff: &Diff<F>) {
69 let data_slice = std::slice::from_mut(&mut self.data);
70 Diff::apply_inplace(data_slice, diff);
71 }
72
73 pub fn apply(&self, diff: &Diff<F>) -> Data<F> {
75 let mut data = Data {
76 data: self.data.clone(),
77 };
78 data.apply_inplace(diff);
79 data
80 }
81}
82
83pub fn init<F: PrimeField>(path: &str, data: &Data<F>) -> std::io::Result<()> {
86 assert!(data.len() <= SRS_SIZE);
88 let mut file = File::create(path)?;
89 for x in &data.data {
90 let x_bytes = encoding::decode_full(*x);
91 file.write_all(&x_bytes)?
92 }
93 Ok(())
94}
95
96pub fn read<F: PrimeField>(path: &str) -> std::io::Result<Data<F>> {
101 let mut file = File::open(path)?;
102 let mut buffer = Vec::new();
103 file.read_to_end(&mut buffer)?;
104 Ok(Data::of_bytes(&buffer))
106}
107
108pub fn update<F: PrimeField>(path: &str, diff: &Diff<F>) -> std::io::Result<()> {
114 let mut file = OpenOptions::new().read(true).write(true).open(path)?;
116 let region_offset = diff.region * (SRS_SIZE as u64);
117 let scalar_size = encoding::encoding_size_full::<F>() as u64;
118 for (index, diff_value) in diff.addresses.iter().zip(diff.diff_values.iter()) {
119 let corresponding_bytes_index = (region_offset + index) * scalar_size;
120 file.seek(SeekFrom::Start(corresponding_bytes_index))?;
121 let new_value: F = {
122 let old_value: F = {
124 let pos = file.stream_position()?;
127 let mut old_value_bytes = vec![0u8; encoding::encoding_size_full::<F>()];
128 file.read_exact(&mut old_value_bytes)?;
129 file.seek(SeekFrom::Start(pos))?;
132 encoding::encode(&old_value_bytes)
133 };
134 old_value + diff_value
135 };
136 let new_value_bytes = encoding::decode_full(new_value);
137 file.write_all(&new_value_bytes)?;
138 }
139 Ok(())
140}
141
142#[cfg(test)]
143mod tests {
144 use crate::{
145 commitment::Commitment, diff::Diff, encoding, storage, storage::Data, Curve, ScalarField,
146 SRS_SIZE,
147 };
148 use ark_ff::{One, UniformRand, Zero};
149 use poly_commitment::ipa::SRS;
150 use rand::Rng;
151 use std::fs;
152 use tempfile::NamedTempFile;
153
154 #[test]
155 fn test_data_consistency() {
159 let mut rng = o1_utils::tests::make_test_rng(None);
160
161 let srs: SRS<Curve> = poly_commitment::precomputed_srs::get_srs_test();
162
163 let file = NamedTempFile::new().unwrap();
165 let path = file.path().to_str().unwrap();
166
167 let data_bytes: Vec<u8> = (0..(SRS_SIZE * (encoding::encoding_size_full::<ScalarField>())))
168 .map(|_| rng.gen())
169 .collect();
170 let mut data = Data::of_bytes(&data_bytes);
171 data.data[0] = ScalarField::zero();
174 let data_comm = data.to_commitment(&srs);
175
176 let read_consistency = {
177 let _init_storage_file = storage::init(path, &data);
178 let read_data = storage::read(path).unwrap();
179 let read_data_comm = read_data.to_commitment(&srs);
180
181 Commitment::eq(&data_comm, &read_data_comm)
183 };
184
185 let (data_updated, update_consistency, diff_comm_consistency) = {
186 let diff = {
187 let nb_updates = std::cmp::max(data.len() / 20, 1);
191 let region = 0;
192 let addresses: Vec<u64> = (0..nb_updates)
193 .map(|_| (rng.gen_range(0..data.len() as u64)))
194 .collect();
195 let mut diff_values: Vec<ScalarField> = addresses
196 .iter()
197 .map(|_| ScalarField::rand(&mut rng))
198 .collect();
199 diff_values[0] = ScalarField::zero() - ScalarField::one();
203 Diff {
204 region,
205 addresses,
206 diff_values,
207 }
208 };
209
210 let updated_data = data.apply(&diff);
211 let updated_data_comm = updated_data.to_commitment(&srs);
212
213 let _file_update = storage::update(path, &diff);
214
215 let updated_read_data = storage::read(path).unwrap();
216 let updated_read_data_comm = updated_read_data.to_commitment(&srs);
217
218 let updated_diff_data_comm = data_comm.update(&srs, diff);
219
220 (
221 Commitment::ne(&updated_data_comm, &data_comm),
223 Commitment::eq(&updated_data_comm, &updated_read_data_comm),
225 Commitment::eq(&updated_diff_data_comm, &updated_data_comm),
227 )
228 };
229
230 let _remove_file = fs::remove_file(path);
231
232 assert!(read_consistency);
233 assert!(data_updated);
234 assert!(update_consistency);
235 assert!(diff_comm_consistency);
236 }
237}